译:在渲染期间更新状态

54 阅读1分钟

背景

我们在工作中使用 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 更新。随着你的应用增长,行为更加可预测。

干杯!!!