闽公网安备 35020302035485号
受控表单是指表单元素的值受 React 组件的 state 或 props 控制。
特点:
.表单元素的值保存在组件的 state 中,以便在需要时进行访问、验证或提交。
.每当用户输入发生变化时,需要手动更新 state 来反映新的值。
.可以通过 state 的值来进行表单元素的验证,并提供实时的错误提示。
import React, { useState } from 'react';
function ControlledForm() {
const [phone, setPhone] = useState('');
const handlePhoneChange = (e) => {
setName(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
// 处理表单提交逻辑
}
return (
<form onSubmit={handleSubmit}>
<label>
Phone:
<input type="text" value={phone} onChange={handlePhoneChange} />
</label>
<button type="submit">Submit</button>
</form>
);
}
export default ControlledForm;
2.2 非受控表单的特点和使用场景非受控表单是指表单元素的值不受 React 组件的 state 或 props 控制,而是将表单数据交给 DOM 节点来处理,可以使用 Ref 来获取数据。
特点:
.表单元素的值不会保存在组件的 state 中,而是通过 DOM 来获取。
.可以通过 ref 来获取表单元素的值,而不需要手动更新 state。import React, { useRef } from 'react';
// 堆代码 duidaima.com
function UncontrolledForm() {
const nameInputRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
const name = nameInputRef.current.value;
}
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" ref={nameInputRef} />
</label>
<button type="submit">Submit</button>
</form>
);
}
export default UncontrolledForm;
2.3 对比受控和非受控表单的差异
| 特点 | 受控表单 | 非受控表单 |
|---|---|---|
| value 管理 | 🙆受控表单元素的值保存在组件的 state 中,方便访问和操作 | 🙅非受控组件需要依赖 ref 来获取元素值,并且会受到组件生命周期变更而影响值 |
| 验证和实时性 | 🙆可以实时验证和处理用户输入 | 🙅不利于实时反映用户输入的值,不方便对用户输入进行验证和处理 |
| 表单的整体控制 | 🙆对表单数据有更好的控制 | 🙅对表单数据的控制有限 |
| 数据流 | 🙆可以根据表单元素的值动态地改变其他组件的状态或行为 | 🙅需要通过 ref 来获取表单元素的值,不符合 React 的数据流思想。 |
| 代码复杂性 | 🙅需要更多的代码来处理表单元素的变化和验证。对于复杂的表单,可能会引入大量的 state 和事件处理函数,导致代码冗长。 | 🙆代码量较少,不需要处理 state 的变化。对于简单的表单,可以更快地实现功能。 |
| dom更新性能 | 🙅 频繁的 setState 触发视图的重新渲染可能会导致性能问题。 | 🙆通过 defaultValue 来设置组件的默认值,它仅会被渲染一次,在后续的渲染时并不起作用 |
| 使用场景 | 基本为最佳实践 | 一般作为简易实现 |

// rc-form-field
// Field.tsx
public reRender() {
if (!this.mounted) return;
this.forceUpdate();
}
.....
public onStoreChange: FieldEntity['onStoreChange'] = (prevStore, namePathList, info) => {
...
case 'remove': {
if (shouldUpdate) {
this.reRender();
return;
}
break;
}
case 'setField': {
if (namePathMatch) {
const { data } = info;
// FieldData 处理,touched/warning/error/validate
...
this.dirty = true;
this.triggerMetaEvent();
// setField 时 field 绑定 name 匹配时强制更新
this.reRender();
return;
}
// setField 携带 shouldUpdate 的控件时更新
if (
shouldUpdate &&
!namePath.length &&
requireUpdate(shouldUpdate, prevStore, store, prevValue, curValue, info)
) {
this.reRender();
return;
}
break;
case 'dependenciesUpdate': {
/**
* 堆代码 duidaima.com
* 当标记了的`dependencies`更新时触发. 相关联的`Field`会更新
*/
const dependencyList = dependencies.map(getNamePath);
// No need for `namePathMath` check and `shouldUpdate` check, since `valueUpdate` will be
// emitted earlier and they will work there
// dependencies 不应和 shouldUpdate 一起使用,可能会导致没必要的 rerender
if (dependencyList.some(dependency => containsNamePath(info.relatedFields, dependency))) {
this.reRender();
return;
}
break;
}
default:
if (
namePathMatch ||
((!dependencies.length || namePath.length || shouldUpdate) &&
requireUpdate(shouldUpdate, prevStore, store, prevValue, curValue, info))
) {
this.reRender();
return;
}
break;

不同于 rc-field-form 中使用的受控表单来做表单状态管理,react-hook-form 使用了 React 的 useRef 和 useReducer 来处理表单数据的状态,而不是使用 React 的 useState 来追踪表单数据的变化。具备非受控表单的优点以提高性能,并使代码更简洁。
react-hook-form 的最简 demo 如下:
import React from "react";
import { useForm } from "react-hook-form";
function MyForm() {
const onSubmit = (data) => {
console.log(data);
};
const { register, handleSubmit, formState: { errors } } = useForm();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName", { required: true })} />
{errors.firstName && <p>First name is required.</p>}
<input {...register("lastName", { required: true })} />
{errors.lastName && <p>Last name is required.</p>}
<button type="submit">Submit</button>
</form>
);
}
为什么会说 react-hook-form 提供的是一个非受控表单,其实就需要细究一下这个 ...register 到底返回了什么// react-hook-form createFormControl const register: UseFormRegister<TFieldValues>
