背景
我们在工作中使用 react-hook-form,因为它是一个很棒的库。基于非受控表单输入,这能带来更好的 UI 性能。
当你需要 React 应用状态和表单状态保持一致时,非受控输入会产生一些棘手的问题。其中一个例子就是来自 API 的默认值。
通常,你会这样设置默认值:
useForm({
defaultValues: {
test_field: "default value",
},
})
当你在首次渲染时不知道这些值时,这种方法就不起作用了。你无法在后续更新它们,因为那时它们就不再是 默认值 了。
那么如何让这个工作呢?
const defaultValue = useSlowValue();
<SmartInput name="test_field" defaultValue={defaultValue} />;
useEffect 方法
你的自然反应可能是使用 effect。你想在 prop 改变时做一些事情。
const SmartInput = (props: { name: string; defaultValue: string }) => {
const { register, setValue } = useFormContext();
useEffect(() => {
setValue(props.name, props.defaultValue);
}, [props.defaultValue]);
return <input {...register(props.name)} />;
};
当 prop 改变时,在字段上设置值。
当 prop 改变时,这可能会导致双重 UI 更新。你在 prop 改变时重新渲染,然后运行 effect,然后再次重新渲染。
随着你的应用增长,你有越来越多这样的 effect,事情开始变得奇怪且难以调试。
渲染期间更新状态方法
你可以通过采用渲染期间更新状态的模式来避免 effect 累积带来的奇怪 bug。
const BetterSmartInput = (props: { name: string; defaultValue: string }) => {
const { register, setValue } = useFormContext();
const [prevDefaultValue, setPrevDefaultValue] = useState(props.defaultValue);
if (prevDefaultValue !== props.defaultValue) {
setValue(props.name, props.defaultValue);
setPrevDefaultValue(props.defaultValue);
}
return <input {...register(props.name)} />;
};
当 React 重新渲染你的组件时,检查 prop 值是否与之前不同并进行更新。这让 React 放弃正在进行的渲染并使用正确的状态重新开始。
没有双重 UI 更新。随着你的应用增长,行为更加可预测。
干杯!!!