在表单中使用 React 组件:受控组件和非受控组件
非受控组件:表单元素的值不是由副组件决定的,是完全内部的状态
通过非受控组件的方式,可以避免某些程度上的组件重复渲染导致的性能问题,但是无法对外有交互。
使用 Hooks 简化表单处理
维护表单组件的状态逻辑,核心在于:字段名,value 值,onChange 事件。
这就是很多组件库里的 useForm 这个功能,可以通过提供字段名去取值和设值,就不需要繁琐地为每个表单元素设置状态。
核心原理:把表单的状态管理单独提取出来, 成为一个可重用的 Hook。
处理表单验证
- 如何定义错误状态
- 如何设置错误状态
可以传递 validators 对象给 useForm 这个 Hook,然后在 hook 内在 setFieldValue 时对其进行验证,并在返回时返回错误信息给调用者。
关键就是在于把表单状态逻辑和 UI 展示逻辑给予 Hooks 进行分离。
import { useState, useMemo, useCallback } from "react";
const useForm = (initialValues = {}, validators) => {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const setFieldValue = useCallback(
(name, value) => {
setValues((values) => ({
...values,
[name]: value,
}));
if (validators[name]) {
const errMsg = validators[name](value);
setErrors((errors) => ({
...errors,
[name]: errMsg || null,
}));
}
},
[validators]
);
return { values, errors, setFieldValue };
};
export default () => {
const validators = useMemo(() => {
return {
name: (value) => {
if (value.length < 2) return "Name length should be no less than 2.";
return null;
},
email: (value) => {
if (!value.includes("@")) return "Invalid email address";
return null;
},
};
}, []);
const { values, errors, setFieldValue } = useForm({}, validators);
const handleSubmit = useCallback(
(evt) => {
evt.preventDefault();
console.log(values);
},
[values]
);
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name: </label>
<input
value={values.name || null}
onChange={(evt) => setFieldValue("name", evt.target.value)}
/>
{errors.name && <span style={{ color: "red" }}>{errors.name}</span>}
</div>
<div>
<label>Email:</label>
<input
value={values.email || null}
onChange={(evt) => setFieldValue("email", evt.target.value)}
/>
{errors.email && <span style={{ color: "red" }}>{errors.email}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
};
思考题
如何在自定义的 useForm Hook 中提供 reset 的 API?
就是返回一个置空的函数。
在自定义的 useForm 内如何实现支持异步验证?