内置浏览器 <select> 组件(https://web.nodejs.cn/en-US/docs/Web/HTML/Element/select)让你可以渲染带有选项的下拉框。

🌐 The built-in browser <select> component lets you render a select box with options.

<select>
<option value="someOption">Some option</option>
<option value="otherOption">Other option</option>
</select>

参考

🌐 Reference

<select>

要显示一个下拉框,请渲染built-in browser <select>组件。

🌐 To display a select box, render the built-in browser <select> component.

<select>
<option value="someOption">Some option</option>
<option value="otherOption">Other option</option>
</select>

查看更多示例。

属性

🌐 Props

<select> 支持所有 常用元素属性

你可以通过传递一个 value 属性来【创建一个受控的下拉框】(#controlling-a-select-box-with-a-state-variable):

🌐 You can make a select box controlled by passing a value prop:

  • value:一个字符串(或 multiple={true} 的字符串数组)。控制选择了哪个选项。每个值字符串都匹配某个嵌套在 <select> 中的 <option>value

当你传递 value 时,你还必须传递一个更新所传值的 onChange 处理器。

🌐 When you pass value, you must also pass an onChange handler that updates the passed value.

如果你的 <select> 无法控制,你可以传递 defaultValue 属性:

🌐 If your <select> is uncontrolled, you may pass the defaultValue prop instead:

这些 <select> 属性对于非受控和受控的选择框都适用:

🌐 These <select> props are relevant both for uncontrolled and controlled select boxes:

  • autoComplete:一个字符串。指定可能的自动补齐行为之一。
  • autoFocus:一个布尔值。如果为 true,React 会在挂载时聚焦该元素。
  • children<select> 接受 <option><optgroup><datalist> 组件作为子组件。你也可以传入你自己的组件,只要它们最终渲染其中一个允许的组件。如果你传入的自定义组件最终渲染 <option> 标签,则你渲染的每个 <option> 必须具有 value
  • disabled:一个布尔值。如果 true,选择框将不可交互,并且看起来是灰色的。
  • form:一个字符串。指定此选择框所属 <form>id。如果省略,则为最近的父表单。
  • multiple:布尔值。如果 true,浏览器允许多重选择
  • name:一个字符串。指定此下拉框的名称,与表单一起提交。
  • onChange:一个 Event 处理函数。用于 受控选择框 是必需的。当用户选择不同的选项时会立即触发。行为类似浏览器的 input 事件
  • onChangeCaptureonChange 的一个版本,会在捕获阶段触发。
  • onInput:一个 Event 处理函数。当值被用户更改时会立即触发。出于历史原因,在 React 中习惯使用 onChange,它的作用类似。
  • onInputCaptureonInput 的一个版本,会在捕获阶段触发。
  • onInvalid:一个Event处理程序函数。如果表单提交时输入验证失败,则会触发。与内置的invalid事件不同,React的onInvalid事件会冒泡。
  • onInvalidCaptureonInvalid 的一个版本,会在捕获阶段触发。
  • required:布尔值。如果 true,则必须提供该值才能提交表单。
  • size:一个数字。对于 multiple={true} 选择项,指定最初可见项目的首选数量。

注意事项

🌐 Caveats

  • 与 HTML 不同,将 selected 属性传递给 <option> 是不被支持的。相反,对于非受控的选择框使用 <select defaultValue>,对于受控的选择框使用 <select value>
  • 如果选择框接收到 value 属性,它将被视为受控。
  • 选择框不能同时受控和不受控。
  • 选择框在其生命周期内无法在受控或不受控之间切换。
  • 每个受控的选择框都需要一个 onChange 事件处理程序,它可以同步更新其备份值。

用法

🌐 Usage

显示带有选项的选择框

🌐 Displaying a select box with options

使用一个 <select> 并在其中包含一列 <option> 组件来显示一个选择框。给每个 <option> 一个 value,表示要随表单提交的数据。

🌐 Render a <select> with a list of <option> components inside to display a select box. Give each <option> a value representing the data to be submitted with the form.

export default function FruitPicker() {
  return (
    <label>
      Pick a fruit:
      <select name="selectedFruit">
        <option value="apple">Apple</option>
        <option value="banana">Banana</option>
        <option value="orange">Orange</option>
      </select>
    </label>
  );
}


为选择框提供标签

🌐 Providing a label for a select box

通常,你会将每个 <select> 放在 <label> 标签内。这告诉浏览器该标签与该选择框相关。当用户点击标签时,浏览器会自动将焦点移动到选择框上。这对于无障碍访问也很重要:当用户关注选择框时,屏幕阅读器会朗读标签的标题。

🌐 Typically, you will place every <select> inside a <label> tag. This tells the browser that this label is associated with that select box. When the user clicks the label, the browser will automatically focus the select box. It’s also essential for accessibility: a screen reader will announce the label caption when the user focuses the select box.

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

🌐 If you can’t nest <select> into a <label>, associate them by passing the same ID to <select id> and <label htmlFor>. To avoid conflicts between multiple instances of one component, generate such an ID with useId.

import { useId } from 'react';

export default function Form() {
  const vegetableSelectId = useId();
  return (
    <>
      <label>
        Pick a fruit:
        <select name="selectedFruit">
          <option value="apple">Apple</option>
          <option value="banana">Banana</option>
          <option value="orange">Orange</option>
        </select>
      </label>
      <hr />
      <label htmlFor={vegetableSelectId}>
        Pick a vegetable:
      </label>
      <select id={vegetableSelectId} name="selectedVegetable">
        <option value="cucumber">Cucumber</option>
        <option value="corn">Corn</option>
        <option value="tomato">Tomato</option>
      </select>
    </>
  );
}


提供最初选择的选项

🌐 Providing an initially selected option

默认情况下,浏览器会选择列表中的第一个 <option>。要默认选择不同的选项,请将该 <option>value 作为 <select> 元素的 defaultValue 传递。

🌐 By default, the browser will select the first <option> in the list. To select a different option by default, pass that <option>’s value as the defaultValue to the <select> element.

export default function FruitPicker() {
  return (
    <label>
      Pick a fruit:
      <select name="selectedFruit" defaultValue="orange">
        <option value="apple">Apple</option>
        <option value="banana">Banana</option>
        <option value="orange">Orange</option>
      </select>
    </label>
  );
}

易犯错误

与 HTML 不同,将 selected 属性传递给单个 <option> 是不支持的。

🌐 Unlike in HTML, passing a selected attribute to an individual <option> is not supported.


启用多选

🌐 Enabling multiple selection

multiple={true} 传递给 <select> 以允许用户选择多个选项。在这种情况下,如果你还指定 defaultValue 来选择初始选中的选项,它必须是一个数组。

🌐 Pass multiple={true} to the <select> to let the user select multiple options. In that case, if you also specify defaultValue to choose the initially selected options, it must be an array.

export default function FruitPicker() {
  return (
    <label>
      Pick some fruits:
      <select
        name="selectedFruit"
        defaultValue={['orange', 'banana']}
        multiple={true}
      >
        <option value="apple">Apple</option>
        <option value="banana">Banana</option>
        <option value="orange">Orange</option>
      </select>
    </label>
  );
}


提交表单时读取选择框的值

🌐 Reading the select box value when submitting a form

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

export default function EditPost() {
  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 });
    // You can generate a URL out of it, as the browser does by default:
    console.log(new URLSearchParams(formData).toString());
    // You can work with it as a plain object.
    const formJson = Object.fromEntries(formData.entries());
    console.log(formJson); // (!) This doesn't include multiple select values
    // Or you can get an array of name-value pairs.
    console.log([...formData.entries()]);
  }

  return (
    <form method="post" onSubmit={handleSubmit}>
      <label>
        Pick your favorite fruit:
        <select name="selectedFruit" defaultValue="orange">
          <option value="apple">Apple</option>
          <option value="banana">Banana</option>
          <option value="orange">Orange</option>
        </select>
      </label>
      <label>
        Pick all your favorite vegetables:
        <select
          name="selectedVegetables"
          multiple={true}
          defaultValue={['corn', 'tomato']}
        >
          <option value="cucumber">Cucumber</option>
          <option value="corn">Corn</option>
          <option value="tomato">Tomato</option>
        </select>
      </label>
      <hr />
      <button type="reset">Reset</button>
      <button type="submit">Submit</button>
    </form>
  );
}

注意

给你的 <select> 一个 name,例如 <select name="selectedFruit" />。你指定的 name 将作为表单数据的键使用,例如 { selectedFruit: "orange" }

🌐 Give a name to your <select>, for example <select name="selectedFruit" />. The name you specified will be used as a key in the form data, for example { selectedFruit: "orange" }.

如果你使用 <select multiple={true}>,你从表单中读取的 FormData 将包含每个选中的值作为单独的名称-值对。仔细查看上面示例中的控制台日志。

🌐 If you use <select multiple={true}>, the FormData you’ll read from the form will include each selected value as a separate name-value pair. Look closely at the console logs in the example above.

易犯错误

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

🌐 By default, any <button> inside a <form> will submit it. This can be surprising! If you have your own custom Button React component, consider returning <button type="button"> instead of <button>. Then, to be explicit, use <button type="submit"> for buttons that are supposed to submit the form.


使用状态变量控制选择框

🌐 Controlling a select box with a state variable

<select /> 这样的选择框是非受控的。即使你传入一个初始选中的值比如 <select defaultValue="orange" />,你的 JSX 只指定了初始值,而不是当前的值。

🌐 A select box like <select /> is uncontrolled. Even if you pass an initially selected value like <select defaultValue="orange" />, your JSX only specifies the initial value, not the value right now.

要渲染一个_受控_的下拉框,请向它传递 value 属性。 React 将强制下拉框始终具有你传递的 value。通常,你可以通过声明一个 状态变量: 来控制下拉框。

function FruitPicker() {
const [selectedFruit, setSelectedFruit] = useState('orange'); // Declare a state variable...
// ...
return (
<select
value={selectedFruit} // ...force the select's value to match the state variable...
onChange={e => setSelectedFruit(e.target.value)} // ... and update the state variable on any change!
>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
</select>
);
}

如果你想要重新渲染 UI 的某些部分以响应每个选择,这将很有用。

🌐 This is useful if you want to re-render some part of the UI in response to every selection.

import { useState } from 'react';

export default function FruitPicker() {
  const [selectedFruit, setSelectedFruit] = useState('orange');
  const [selectedVegs, setSelectedVegs] = useState(['corn', 'tomato']);
  return (
    <>
      <label>
        Pick a fruit:
        <select
          value={selectedFruit}
          onChange={e => setSelectedFruit(e.target.value)}
        >
          <option value="apple">Apple</option>
          <option value="banana">Banana</option>
          <option value="orange">Orange</option>
        </select>
      </label>
      <hr />
      <label>
        Pick all your favorite vegetables:
        <select
          multiple={true}
          value={selectedVegs}
          onChange={e => {
            const options = [...e.target.selectedOptions];
            const values = options.map(option => option.value);
            setSelectedVegs(values);
          }}
        >
          <option value="cucumber">Cucumber</option>
          <option value="corn">Corn</option>
          <option value="tomato">Tomato</option>
        </select>
      </label>
      <hr />
      <p>Your favorite fruit: {selectedFruit}</p>
      <p>Your favorite vegetables: {selectedVegs.join(', ')}</p>
    </>
  );
}

易犯错误

如果你传递 value 而没有 onChange,将无法选择选项。 当你通过传递某个 value 来控制一个下拉框时,你会强制它始终拥有你传入的值。因此,如果你将一个状态变量作为 value 传入,但在 onChange 事件处理函数中忘记同步更新该状态变量,React 将在每次按键后将下拉框恢复到你指定的 value

与 HTML 不同,将 selected 属性传递给单个 <option> 是不支持的。

🌐 Unlike in HTML, passing a selected attribute to an individual <option> is not supported.