自定义挂钩中的同步状态

时间:2020-07-27 20:13:39

标签: javascript reactjs synchronization react-hooks state

TL / DR:
如何正确同步两个挂钩之间的状态?

实施细节:

我将简化示例并使用useState,而不是为了简化问题而实际使用的第一个自定义钩子。

基本上,被忽略的钩子所做的是它返回一个state和辅助函数。所以只画图片:

const [email, onEmailChange, onEmailBlur] = useField('init', isEmailValid)

仅用替换了相关问题(尽管此处在技术上是多余的)

 const [email, setEmail] = useState('hi')

我将本节摘录留在这里的原因是,这样我就不会得到最初的useState多余的评论/答案

有效的方法
将状态引用传递给只返回它的基本函数。

const useForm = (...fields) => {
  return {
    fields,
  }
}
const form = useForm(email) // updates correctly

问题开始的地方
现在显然,这件事甚至不需要钩子。当我尝试添加更复杂的逻辑时出现问题

const useForm = (submitFunction, ...fields) => {
  const initialForm = {
    fields,
    wasSubmitted: false,
  }
  const [form, setForm] = useState(initialForm)

  const handleSubmit = (event) => {
    event.preventDefault()
    if (!fields.some(({ error }) => !!error) {
       submitFunction()
    }
    setForm({
      fields,
      wasSubmitted: true,
    })
  }

  return [form, handleSubmit] // now the state no longer updates
}

现在我确实怀疑为什么会这样。仅仅因为useState初始化了状态的新实例,所以我们只是从传递的状态返回初始值。

问题是,关于如何同步这些内容,我无法真正解决。

具有工作钩和非工作钩的可编辑示例

Edit vibrant-darwin-8s1cv

1 个答案:

答案 0 :(得分:1)

正如您提到的,这是因为useState中的useForm设置了form状态的初始值,并且其值在下一个渲染中不会改变。

如果要保持formfields同步,则每次useEffect中的值时,都可以使用form更新fields的值。变化。

...
const [form, setForm] = useState<Form>(initialForm);

useEffect(() => {
  setForm(formState => ({ ...formState, fields }))
}, fields);
...

此解决方案的问题在于,setForm中的useEffect每次更改fields中的值都会引起另一次重新渲染。

解决此问题的另一种方法是不在状态中保存fields的值,而仅将wasSubmitted保持在useForm的状态。

const useForm = (submitFunction, ...fields) => {
  const [wasSubmitted, setWasSubmitted] = useState(false);

  const handleSubmit = (event) => {
    if (fields.some(field => field === "run")) {
      submitFunction();
    }
    setWasSubmitted(true);
  };

  return [{ fields, wasSubmitted }, handleSubmit];
};