通过内置浏览器 <input> 组件,您可以渲染不同类型的表单输入。

<input />

参考

<input>

要显示输入,请渲染内置浏览器 <input> 组件。

<input name="myInput" />

请在下方查看更多示例。

属性

<input> 支持所有 通用元素属性。

Canary 版本

React 对 formAction 属性的扩展目前仅在 React 的 Canary 和实验性渠道中可用。在 React 的稳定版本中,formAction 仅作为 内置浏览器 HTML 组件 工作。在此处详细了解 React 的发布渠道

formAction: 字符串或函数。覆盖 type="submit"type="image" 的父级 <form action>。当 URL 传递给 action 时,表单的行为将类似于标准 HTML 表单。当函数传递给 formAction 时,该函数将处理表单提交。请参阅 <form action>

您可以通过传递以下属性之一来 使输入受控

  • checked: 布尔值。对于复选框输入或单选按钮,控制其是否被选中。
  • value:一个字符串。对于文本输入,控制其文本。(对于单选按钮,指定其表单数据。)

当你传递它们中的任何一个时,你还必须传递一个 onChange 处理程序,用于更新传递的值。

这些 <input> 属性仅与非受控输入相关。

这些 <input> 属性与非受控和受控输入相关。

  • accept:一个字符串。指定 type="file" 输入接受的文件类型。
  • alt:一个字符串。指定 type="image" 输入的替代图像文本。
  • capture:一个字符串。指定由 type="file" 输入捕获的媒体(麦克风、视频或摄像头)。
  • autoComplete:一个字符串。指定一种可能的自动完成行为
  • autoFocus:一个布尔值。如果为 true,React 将在挂载时聚焦该元素。
  • dirname:一个字符串。指定元素方向的表单字段名称。
  • disabled:一个布尔值。如果为 true,则输入将不可交互,并且会显示为灰色。
  • children<input> 不接受子元素。
  • form:一个字符串。指定此输入所属的 <form>id。如果省略,则为最近的父表单。
  • formAction:一个字符串。覆盖 type="submit"type="image" 的父 <form action>
  • formEnctype:一个字符串。覆盖 type="submit"type="image" 的父 <form enctype>
  • formMethod:一个字符串。覆盖 type="submit"type="image" 的父 <form method>
  • formNoValidate:一个字符串。覆盖 type="submit"type="image" 的父 <form noValidate>
  • formTarget:一个字符串。覆盖 type="submit"type="image" 的父 <form target>
  • height:字符串。指定 type="image" 的图像高度。
  • list:字符串。指定包含自动完成选项的 <datalist>id
  • max:数字。指定数值和日期时间输入的最大值。
  • maxLength:数字。指定文本和其他输入的最大长度。
  • min:数字。指定数值和日期时间输入的最小值。
  • minLength:数字。指定文本和其他输入的最小长度。
  • multiple:布尔值。指定 <type="file"type="email" 是否允许多个值。
  • name:字符串。指定此输入的名称,该名称将随表单一起提交。
  • onChangeEvent 处理程序函数。 受控输入必需。当用户的操作导致输入的值发生更改时(例如,每次按键都会触发),该函数会立即触发。其行为类似于浏览器的 input 事件。
  • onChangeCapture:在 捕获阶段触发的 onChange 版本。
  • onInputEvent 处理程序函数。当用户更改值时立即触发。出于历史原因,在 React 中,习惯上使用 onChange 来代替,它的工作原理类似。
  • onInputCapture:在 捕获阶段触发的 onInput 版本。
  • onInvalidEvent 处理程序函数。如果输入在表单提交时验证失败,则触发。与内置的 invalid 事件不同,React 的 onInvalid 事件会冒泡。
  • onInvalidCapture:在 捕获阶段触发的 onInvalid 版本。
  • onSelectEvent 处理程序函数。在 <input> 内的选中内容发生更改后触发。React 扩展了 onSelect 事件,使其也能在清空选择和编辑(这可能会影响选择)时触发。
  • onSelectCapture:在 捕获阶段触发的 onSelect 版本。
  • pattern:字符串。指定 value 必须匹配的模式。
  • placeholder:字符串。当输入值为空时,以灰色显示。
  • readOnly:布尔值。如果为 true,则用户无法编辑输入。
  • required:布尔值。如果为 true,则必须提供值才能提交表单。
  • size:一个数字。类似于设置宽度,但单位取决于控件。
  • src:一个字符串。指定 type="image" 输入的图像源。
  • step:一个正数或 'any' 字符串。指定有效值之间的距离。
  • type:一个字符串。属于输入类型 之一。
  • width:一个字符串。指定 type="image" 输入的图像宽度。

注意事项

  • 复选框需要 checked(或 defaultChecked),而不是 value(或 defaultValue)。
  • 如果文本输入接收字符串 value 属性,它将被视为受控的。
  • 如果复选框或单选按钮接收布尔值 checked 属性,它将被视为受控的。
  • 输入不能同时是受控的和不受控的。
  • 输入在其生命周期内不能在受控和不受控之间切换。
  • 每个受控输入都需要一个 onChange 事件处理程序,该程序同步更新其支持值。

用法

显示不同类型的输入

要显示输入,请渲染一个 <input> 组件。默认情况下,它将是一个文本输入。您可以传递 type="checkbox" 来表示复选框,传递 type="radio" 来表示单选按钮,或者传递其他输入类型之一。

export default function MyForm() {
  return (
    <>
      <label>
        Text input: <input name="myInput" />
      </label>
      <hr />
      <label>
        Checkbox: <input type="checkbox" name="myCheckbox" />
      </label>
      <hr />
      <p>
        Radio buttons:
        <label>
          <input type="radio" name="myRadio" value="option1" />
          Option 1
        </label>
        <label>
          <input type="radio" name="myRadio" value="option2" />
          Option 2
        </label>
        <label>
          <input type="radio" name="myRadio" value="option3" />
          Option 3
        </label>
      </p>
    </>
  );
}


为输入提供标签

通常,您会将每个 <input> 放置在 <label> 标签内。这会告诉浏览器该标签与该输入相关联。当用户单击标签时,浏览器将自动聚焦输入。这对可访问性也很重要:当用户聚焦相关输入时,屏幕阅读器会播报标签标题。

如果无法将 <input> 嵌套到 <label> 中,请通过将相同的 ID 传递给 <input id><label htmlFor> 来关联它们。为了避免一个组件的多个实例之间发生冲突,请使用 useId 生成这样的 ID。

import { useId } from 'react';

export default function Form() {
  const ageInputId = useId();
  return (
    <>
      <label>
        Your first name:
        <input name="firstName" />
      </label>
      <hr />
      <label htmlFor={ageInputId}>Your age:</label>
      <input id={ageInputId} name="age" type="number" />
    </>
  );
}


为输入提供初始值

您可以选择为任何输入指定初始值。对于文本输入,请将其作为 defaultValue 字符串传递。复选框和单选按钮应使用 defaultChecked 布尔值指定初始值。

export default function MyForm() {
  return (
    <>
      <label>
        Text input: <input name="myInput" defaultValue="Some initial value" />
      </label>
      <hr />
      <label>
        Checkbox: <input type="checkbox" name="myCheckbox" defaultChecked={true} />
      </label>
      <hr />
      <p>
        Radio buttons:
        <label>
          <input type="radio" name="myRadio" value="option1" />
          Option 1
        </label>
        <label>
          <input
            type="radio"
            name="myRadio"
            value="option2"
            defaultChecked={true} 
          />
          Option 2
        </label>
        <label>
          <input type="radio" name="myRadio" value="option3" />
          Option 3
        </label>
      </p>
    </>
  );
}


提交表单时读取输入值

在您的输入周围添加一个带有 <button type="submit"><form>。它将调用您的 <form onSubmit> 事件处理程序。默认情况下,浏览器会将表单数据发送到当前 URL 并刷新页面。您可以通过调用 e.preventDefault() 来覆盖该行为。使用 new FormData(e.target) 读取表单数据。

export default function MyForm() {
  function handleSubmit(e) {
    // Prevent the browser from reloading the page
    e.preventDefault();

    // Read the form data
    const form = e.target;
    const formData = new FormData(form);

    // You can pass formData as a fetch body directly:
    fetch('/some-api', { method: form.method, body: formData });

    // Or you can work with it as a plain object:
    const formJson = Object.fromEntries(formData.entries());
    console.log(formJson);
  }

  return (
    <form method="post" onSubmit={handleSubmit}>
      <label>
        Text input: <input name="myInput" defaultValue="Some initial value" />
      </label>
      <hr />
      <label>
        Checkbox: <input type="checkbox" name="myCheckbox" defaultChecked={true} />
      </label>
      <hr />
      <p>
        Radio buttons:
        <label><input type="radio" name="myRadio" value="option1" /> Option 1</label>
        <label><input type="radio" name="myRadio" value="option2" defaultChecked={true} /> Option 2</label>
        <label><input type="radio" name="myRadio" value="option3" /> Option 3</label>
      </p>
      <hr />
      <button type="reset">Reset form</button>
      <button type="submit">Submit form</button>
    </form>
  );
}

注意

为每个 <input> 指定一个 name,例如 <input name="firstName" defaultValue="Taylor" />。您指定的 name 将用作表单数据中的键,例如 { firstName: "Taylor" }

陷阱

默认情况下,<form> 内的_任何_ <button> 都会提交它。这可能会让人感到意外!如果您有自己的自定义 Button React 组件,请考虑返回 <button type="button"> 而不是 <button>。然后,为了明确起见,对_应该_提交表单的按钮使用 <button type="submit">


使用状态变量控制输入

<input /> 这样的输入是_不受控的_。即使您 传递了初始值,例如 <input defaultValue="初始文本" />,您的 JSX 也只指定了初始值。它不控制现在的值应该是什么。

要渲染_受控_输入,请将 value 属性传递给它(对于复选框和单选按钮,则传递 checked)。React 将强制输入始终具有您传递的 value。通常,您可以通过声明一个 状态变量 来做到这一点:

function Form() {
const [firstName, setFirstName] = useState(''); // Declare a state variable...
// ...
return (
<input
value={firstName} // ...force the input's value to match the state variable...
onChange={e => setFirstName(e.target.value)} // ... and update the state variable on any edits!
/>
);
}

如果您无论如何都需要状态,例如在每次编辑时重新渲染 UI,那么受控输入是有意义的

function Form() {
const [firstName, setFirstName] = useState('');
return (
<>
<label>
First name:
<input value={firstName} onChange={e => setFirstName(e.target.value)} />
</label>
{firstName !== '' && <p>Your name is {firstName}.</p>}
...

如果您想提供多种方法来调整输入状态(例如,通过单击按钮),它也很有用

function Form() {
// ...
const [age, setAge] = useState('');
const ageAsNumber = Number(age);
return (
<>
<label>
Age:
<input
value={age}
onChange={e => setAge(e.target.value)}
type="number"
/>
<button onClick={() => setAge(ageAsNumber + 10)}>
Add 10 years
</button>

您传递给受控组件的 value 不应该是 undefinednull。如果您需要将初始值设置为空(例如,下面的 firstName 字段),请将您的状态变量初始化为空字符串 ('')。

import { useState } from 'react';

export default function Form() {
  const [firstName, setFirstName] = useState('');
  const [age, setAge] = useState('20');
  const ageAsNumber = Number(age);
  return (
    <>
      <label>
        First name:
        <input
          value={firstName}
          onChange={e => setFirstName(e.target.value)}
        />
      </label>
      <label>
        Age:
        <input
          value={age}
          onChange={e => setAge(e.target.value)}
          type="number"
        />
        <button onClick={() => setAge(ageAsNumber + 10)}>
          Add 10 years
        </button>
      </label>
      {firstName !== '' &&
        <p>Your name is {firstName}.</p>
      }
      {ageAsNumber > 0 &&
        <p>Your age is {ageAsNumber}.</p>
      }
    </>
  );
}

陷阱

如果您传递了 value 但没有传递 onChange,则将无法在输入中键入内容。当您通过传递某个 value 来控制输入时,您_强制_它始终具有您传递的值。因此,如果您传递一个状态变量作为 value,但忘记在 onChange 事件处理程序中同步更新该状态变量,React 将在每次按键后将输入恢复为您指定的 value


优化每次按键时的重新渲染

当您使用受控输入时,您会在每次按键时设置状态。如果包含您的状态的组件重新渲染了一棵很大的树,这可能会变慢。您可以通过几种方法来优化重新渲染性能。

例如,假设您从一个表单开始,该表单在每次按键时都会重新渲染所有页面内容

function App() {
const [firstName, setFirstName] = useState('');
return (
<>
<form>
<input value={firstName} onChange={e => setFirstName(e.target.value)} />
</form>
<PageContent />
</>
);
}

由于 <PageContent /> 不依赖于输入状态,您可以将输入状态移动到它自己的组件中

function App() {
return (
<>
<SignupForm />
<PageContent />
</>
);
}

function SignupForm() {
const [firstName, setFirstName] = useState('');
return (
<form>
<input value={firstName} onChange={e => setFirstName(e.target.value)} />
</form>
);
}

这显着提高了性能,因为现在只有 SignupForm 会在每次按键时重新渲染。

如果无法避免重新渲染(例如,如果 PageContent 依赖于搜索输入的值),useDeferredValue 允许您即使在大型重新渲染过程中也能保持受控输入的响应。


故障排除

当我输入内容时,我的文本输入框没有更新

如果你使用 value 属性渲染了一个输入框,但没有设置 onChange 属性,你将在控制台中看到一个错误

// 🔴 Bug: controlled text input with no onChange handler
<input value={something} />
控制台
你为表单字段提供了一个 value 属性,但没有设置 onChange 处理程序。这将渲染一个只读字段。如果该字段应该是可改变的,请使用 defaultValue 属性。否则,请设置 onChangereadOnly 属性。

正如错误信息所建议的,如果你只想指定初始值,请传递 defaultValue 属性

// ✅ Good: uncontrolled input with an initial value
<input defaultValue={something} />

如果你想使用状态变量控制此输入,请指定一个 onChange 处理程序

// ✅ Good: controlled input with onChange
<input value={something} onChange={e => setSomething(e.target.value)} />

如果该值有意为只读,请添加 readOnly 属性以抑制错误

// ✅ Good: readonly controlled input without on change
<input value={something} readOnly={true} />

当我点击我的复选框时,它没有更新

如果你使用 checked 属性渲染了一个复选框,但没有设置 onChange 属性,你将在控制台中看到一个错误

// 🔴 Bug: controlled checkbox with no onChange handler
<input type="checkbox" checked={something} />
控制台
你为表单字段提供了一个 checked 属性,但没有设置 onChange 处理程序。这将渲染一个只读字段。如果该字段应该是可改变的,请使用 defaultChecked 属性。否则,请设置 onChangereadOnly 属性。

正如错误信息所建议的,如果你只想指定初始值,请传递 defaultChecked 属性

// ✅ Good: uncontrolled checkbox with an initial value
<input type="checkbox" defaultChecked={something} />

如果你想使用状态变量控制此复选框,请指定一个 onChange 处理程序

// ✅ Good: controlled checkbox with onChange
<input type="checkbox" checked={something} onChange={e => setSomething(e.target.checked)} />

陷阱

你需要读取 e.target.checked 而不是 e.target.value 来获取复选框的值。

如果复选框有意为只读,请添加 readOnly 属性以抑制错误

// ✅ Good: readonly controlled input without on change
<input type="checkbox" checked={something} readOnly={true} />

我的输入光标在每次按键时都会跳到开头

如果你控制了一个输入框,你必须在 onChange 期间将其状态变量更新为 DOM 中的输入值。

你不能将其更新为 e.target.value(或复选框的 e.target.checked)以外的值

function handleChange(e) {
// 🔴 Bug: updating an input to something other than e.target.value
setFirstName(e.target.value.toUpperCase());
}

你也不能异步更新它

function handleChange(e) {
// 🔴 Bug: updating an input asynchronously
setTimeout(() => {
setFirstName(e.target.value);
}, 100);
}

要修复你的代码,请将其同步更新为 e.target.value

function handleChange(e) {
// ✅ Updating a controlled input to e.target.value synchronously
setFirstName(e.target.value);
}

如果这不能解决问题,则可能是输入框在每次按键时都会从 DOM 中删除并重新添加。如果你不小心在每次重新渲染时都重置状态,例如,如果输入框或其父元素之一始终接收到不同的 key 属性,或者如果你嵌套组件函数定义(这是不支持的,并且会导致“内部”组件始终被视为不同的树),则可能会发生这种情况。


我收到一个错误:“组件正在将一个不受控的输入更改为受控的”

如果你为组件提供了一个 value 属性,则它在其整个生命周期内都必须保持为字符串。

你不能先传递 value={undefined},然后再传递 value="some string",因为 React 无法知道你是希望该组件是不受控的还是受控的。受控组件应该始终接收一个字符串类型的 value 属性,而不是 nullundefined

如果您的 value 来自 API 或状态变量,则它可能初始化为 nullundefined。在这种情况下,请在初始时将其设置为空字符串 (''),或传递 value={someValue ?? ''} 以确保 value 是字符串。

同样,如果您将 checked 传递给复选框,请确保它始终为布尔值。