使

使用 React Hook 表单控制器将任何东西变成表单字段进入控制器制作字段组件真实示例结论

2025-06-08

使用 React Hook 表单控制器将任何内容转换为表单字段

进入控制器

制作字段组件

真实案例

结论

封面图片由Chris J. DavisUnsplash上拍摄

React Hook Form很快就成了我最喜欢的处理各种形状和大小表单的库,主要是因为它出色的开发体验。他们主页上 30 秒的截屏视频很好地演示了如何使用 的魔力register连接每个字段,将其集成到标准表单中。使用原生<input/>组件时,启动和运行起来非常简单。

但在现实世界中,我们通常不会使用原生输入。流行的 UI 库通常会抽象并包装所有底层表单元素,这使得使用 变得困难甚至无法实现register

有时,我们希望通过自定义交互组件来提升用户的体验,比如用 5 个真正的星形图标来评价产品,而不是使用枯燥的选择框。那么,如何才能将这些组件与现有表单轻松关联,避免逻辑混乱呢?

进入控制器

该库导出了一个<Controller/>专为此目的而制作的组件。它允许我们将任何组件连接到表单,使其能够显示和设置其值。

要使用它,你需要从而不是control返回的对象。此外,像往常一样,你需要一个来告诉表单我们正在控制哪个字段。最后,prop 是我们放置组件的地方。useForm()registernamerender

// Controller syntax

const { control } = useForm();

return (
  <Controller
    control={control}
    name="myField"
    render={/* Custom field component goes here */}
  />
);
Enter fullscreen mode Exit fullscreen mode

制作字段组件

为什么这样称呼Controller?可能是因为我们的字段组件需要成为一个受控组件

简而言之,受控组件是通过 props 获取和设置其当前“状态”的组件。对于表单字段来说,该状态就是该字段的当前值。

<input/>是一个可控制组件的例子。我们告诉输入它的当前值是什么,并让它知道何时应该更改该值。

// <input/> as a controlled component in a standard React form

const [val, setVal] = useState('')

return (
  <input
    type="text"
    value={val}
    onChange={e => setVal(e.target.value)}
  />
)
Enter fullscreen mode Exit fullscreen mode

这里我们看到了使我们的字段组件与控制器一起工作所需的两个道具:

  1. value- 它应该显示该字段的当前值。
  2. onChange- 它应该能够通知控制器当前值何时发生变化。

这也恰好是函数传递给我们的两个属性render!它的签名包括一个field具有valueonChange(以及其他东西)的对象。

使用控制器进行基本输入没有多大意义,但这里是为了说明目的:

// Using a basic input in a Controller
// (though you can just use `register` here)

const { control } = useForm();

return (
  <>
    <Controller
      control={control}
      name="myField"
      render={({ field: { value, onChange }}) => (
        <input value={value} onChange={onChange} />
      )}
    />
  </>
)
Enter fullscreen mode Exit fullscreen mode

注意:如果您使用的是 React Hook Form V6 或更早版本,此处的函数签名略有不同。value并且onChange是参数的顶级属性,如下所示。

// V6 or earlier
render=({ value, onChange }) => (
  <input value={value} onChange={onChange}  />
)

真实案例

使用 UI 库:Material UI

许多项目使用流行的 UI 库(例如Material UI)的表单输入。问题在于,这些<input/>组件通常对我们隐藏,因此我们无法将register它们连接到表单。这时,Controller 就派上用场了!

通常,字段会使用相同的valueproponChange名称。如果是这种情况,我们可以简单地将{...field}对象展开到组件中。

其他时候,props 的命名可能不一样。例如,Checkbox 接受的值是checked而不是value。这意味着我们无法轻易地field向其中扩展,但结果仍然相当容易组合。

export default function App() {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      textField: "",
      checkbox: false
    }
  });

  const onSubmit = (values) => alert(JSON.stringify(values));

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        control={control}
        name="textField"
        render={({ field }) => (
          // Material UI TextField already supports
          // `value` and `onChange`
          <TextField {...field} label="Text field" />
        )}
      />

      <Controller
        control={control}
        name="checkbox"
        render={({ field: { value, onChange } }) => (
          // Checkbox accepts its value as `checked`
          // so we need to connect the props here
          <FormControlLabel
            control={<Checkbox checked={value} onChange={onChange} />}
            label="I am a checkbox"
          />
        )}
      />

      <Button type="submit" variant="contained" color="primary">
        Submit
      </Button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

查看 Code Sandbox 上的完整示例

从零开始构建:五星级评级领域

我们可能都用过那种随处可见的小部件,它允许我们通过点击一排星形图标来对任何内容进行评分。值得庆幸的是,如果我们能够创建一个受控组件,就可以将其整齐地嵌入到表单的其余部分中。

// StarButton displays a single star
// It is controlled via active and onClick props
const StarButton = ({ active, onClick }) => (
  <button type="button" onClick={onClick}>
    {active ? <Star color="secondary" /> : <StarBorder />}
  </button>
);

// StarField uses 5 StarButtons to create a field
// with value and onChange props
const StarField = ({ value, onChange }) => (
  <>
    <StarButton active={value >= 1} onClick={() => onChange(1)} />
    <StarButton active={value >= 2} onClick={() => onChange(2)} />
    <StarButton active={value >= 3} onClick={() => onChange(3)} />
    <StarButton active={value >= 4} onClick={() => onChange(4)} />
    <StarButton active={value >= 5} onClick={() => onChange(5)} />
  </>
);

export default function App() {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      rating: 0
    }
  });

  const onSubmit = ({ rating }) => {
    alert(`Your rating: ${rating}`);
  };

  return (
    <Container>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          control={control}
          name="rating"
          render={({ field }) => <StarField {...field} />}
        />

        <Button type="submit">Submit</Button>
      </form>
    </Container>
  );
}
Enter fullscreen mode Exit fullscreen mode

查看 Code Sandbox 上的完整示例

结论

使用<Controller/>一个控制得当的组件,你几乎可以将任何东西变成与 React Hook Form 兼容的表单字段。这个字段可以简单也可以复杂,可以封装任何逻辑,只要它能做到以下两点:

  1. 接收并呈现字段的当前值/状态,通常通过valueprop。
  2. 当该值需要更新时调用一个函数,通常通过onChangeprop。
链接:https://dev.to/elyngved/turn-anything-into-a-form-field-with-react-hook-form-controller-42c
PREV
我为开发人员制作了一个投资组合生成器 - 欢迎反馈!
NEXT
我最喜欢的 5 份新闻通讯,用于了解前端开发的最新动态