传递给其他功能组件的回调是否在刷新时更新?

Are callbacks that are passed into other functional components updated on refresh?

我有一个复杂的表单,其中包含不同类型的输入元素。为简单起见,我将其限制为一个,因为每个有效地遵循相同的方法。

const Form = () => {
  const [state, setState] = useState();

  const handleChange = useCallback((name, value) => {
    setState({
      ...state,
      [name]: value
    })
  }, [state, setState]);

  useEffect(() => {
    console.log(state)
  }, [state, setState]);

  return (
    <div>
      <Input name="inputOne" onChange={handleChange} />
      <Input name="inputTwo" onChange={handleChange} />
    </div>
  );
}

const Input = ({ name, onChange }) => {
  const [value, setValue] = useState("");

  const handleChange = useCallback((e) => {
    setValue(e.target.value);
    onChange(name, e.target.value)
  }, [value, setValue]);

  return (
    <input onChange={handleChange} value={value}/>
  );
}

出于某种原因,在使用此表单时。如果我在第一个输入中输入值,我得到的结果记录为

{ "inputOne": "value entered into input one" }

但是如果我在第二个输入中输入一些东西,我会得到这个

{ "inputTwo": "value entered into input two" }

如果我返回并再次输入 inputOne,我们会再次得到这个。

{ "inputOne": "value entered into input one, and more text entered" }   

据我所知,我编写的代码应该在状态更改时更新回调,以便传播的新状态 (...state) 是最新的状态值。然后在我将 handleChange 函数传递到的所有元素中更新它。但是,似乎 handleChange 在传递到 onChange 参数的地方没有更新,因此调用旧回调时状态 属性 被错误保存。

谁能告诉我这是否正确以及 handleChange 回调没有正确更新?就像两个 Input 都看到了对 handleChange 回调的不同引用。

你的问题是一个非常典型的闭包问题

旧参考资料的获取方式非常复杂,但让我尽量解释一下

当您创建表单组件时,您的 handleChange 函数会在每次状态更改时更新,并传递给 Input 组件。然而,即使更新的实例传递给 child,child 也不会使用更新的实例,除非它自己的值发生变化。现在,当它尝试更新其值时,它正在调用旧的引用函数,该函数中没有反映其他输入值。

有两种方法可以解决这个问题

  • 使用回调方法来设置状态,这样无论闭包中的值如何,您都可以通过 React 获得最新状态
  • 解决它的另一种方法是在 Input 组件中添加 onChange 作为对 useCallback 的依赖,这样即使其他输入元素导致状态更改,所有 Input 都有 onChange 函数的最新引用

const { useCallback, useState, useEffect } = React;
const Form = () => {
  const [state, setState] = useState({});

  const handleChange = useCallback((name, value) => {
    setState(prev=> ({
      ...prev,
      [name]: value
    }))
  }, [setState]);

  useEffect(() => {
    console.log(state);
  }, [state, setState]);

  return (
    <div>
      <Input name="inputOne" onChange={handleChange} />
      <Input name="inputTwo" onChange={handleChange} />
    </div>
  );
}

const Input = ({ name, onChange }) => {
  const [value, setValue] = useState("");

  const handleChange = useCallback((e) => {
    setValue(e.target.value);
    onChange(name, e.target.value)
  }, [value, setValue]);

  return (
    <input onChange={handleChange} value={value}/>
  );
}

ReactDOM.render(<Form />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />

P.S. 如果您正在使用 useCallback 并将状态添加为依赖项并且您在自身内部更新状态,则它不会实现任何目标每次更新都会创建一个单独的实例。 最好使用 setState 的函数式方法来解决这个问题并更好地记忆