React 表单工程化:用 react-hook-form 把受控表单的性能陷阱填平
test12026-09-222 次阅读
受控表单为什么越写越卡
React 里最常见的一种表单写法是把每个字段都塞进 useState,再在 onChange 里 setState。单个输入框没问题,但当表单字段变多、或在长列表里嵌表单时,每次按键都会触发整棵组件树重渲染。受控意味着输入值来自 state、再回写 state,输入法组合、自动补全都会加剧渲染次数。
把表单交给 react-hook-form
react-hook-form 的核心思路是非受控优先:它用 ref 直接读写 DOM 节点,只在提交或校验时读取字段值,因此输入过程几乎不触发 React 渲染。下面是一段可直接运行的登录表单。
import { useForm } from "react-hook-form";
type LoginForm = {
email: string;
password: string;
};
export function LoginForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginForm>({ defaultValues: { email: "", password: "" } });
const onSubmit = handleSubmit(async (values) => {
await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(values),
});
});
return (
<form onSubmit={onSubmit}>
<input
{...register("email", {
required: "邮箱必填",
pattern: { value: /^\S+@\S+$/, message: "邮箱格式不正确" },
})}
/>
{errors.email && <span>{errors.email.message}</span>}
<input type="password" {...register("password", { required: "密码必填" })} />
<button disabled={isSubmitting}>登录</button>
</form>
);
}
register 把 onChange、onBlur、ref、name 一起挂到 input 上,校验规则写在第二个参数里,错误信息从 formState.errors 读取,提交状态用 isSubmitting 即可禁用按钮。
自定义组件怎么接
第三方组件(日期选择器、级联选择)拿不到 ref,用 Controller 包一层,把 field.value 和 field.onChange 透传给组件即可。
import { Controller, useForm } from "react-hook-form";
function RoleSelect({ value, onChange }: { value: string; onChange: (v: string) => void }) {
return <select value={value} onChange={(e) => onChange(e.target.value)} />;
}
export function ProfileForm() {
const { control } = useForm({ defaultValues: { role: "user" } });
return (
<Controller
name="role"
control={control}
render={({ field }) => (
<RoleSelect value={field.value} onChange={field.onChange} />
)}
/>
);
}
什么时候该用
- 字段多、嵌套深、或在列表里批量渲染表单时,react-hook-form 的非受控模型能显著减少渲染。
- 简单单行搜索框、单一开关这类轻量场景,受控写法更直观,没必要引入依赖。
- 需要跨字段联动校验、动态字段数组时,配合 useFieldArray 比手写 state 省心得多。
T
test1
文章作者
受控表单为什么越写越卡 React 里最常见的一种表单写法是把每个字段都塞进 useState,再在 onChange...
- 分类
- 技术
- 发布时间
- 2026-09-22
- 字数
- 约 2156 字
- 阅读
- 2 次
