React中的useEffect内设置setTimeout和setState方法

3
我有这段代码,我的问题是为什么在我的答案函数中我得到了初始状态。如何以正确的方式设置状态,以便在setTimeout函数的回调中获得正确的状态?
const App = () => {
  const [state, setState] = useState({
    name: "",
    password: "",
  });

  useEffect(() => {
    setState({ ...state, password: "hello" });
    setTimeout(answer, 1000);
  }, []);

  const answer = () => {
    console.log(state);
    // we get initial State
  };
  return <div className="App"></div>;
};
2个回答

6
原因在于闭包。
当调用setTimeout()函数时,它会关闭传递给它的state的值,并将其记录下来,这就是为什么answer函数始终记录setTimeout()函数调用时state的值。
在你的代码中,由于setTimeout()函数在state包含带有空值的对象时被调用,所以answer函数记录了该值,而不是记录更新后的值。
要记录最新的state值,可以使用useRef()钩子。这个钩子返回一个对象,其中包含一个名为current的属性,此属性的值是传递给useRef()的参数的值。

function App() {
  const [state, setState] = React.useState({
    name: "",
    password: "",
  });

  const stateRef = React.useRef(state);

  React.useEffect(() => {
    stateRef.current = { ...stateRef.current, password: 'hello'};
    setState(stateRef.current);
    setTimeout(answer, 1000);
  }, []);

  const answer = () => {
    console.log(stateRef.current);
  };
  return <div></div>;
}

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


这段代码的一个问题是我们需要更新状态两次,嗯。。 - Lekar123
不,状态只更新一次,stateRef对象不是您组件的状态,但当您想要更新state时,您需要更新stateRef - Yousaf
是的,这就是我说的。 - Lekar123
你可以创建一个函数来更新stateRefstate,并且只需在需要更新状态时调用该函数。 - Yousaf

0

函数 answer 是一个闭包,它捕获了在其定义时作用域内的变量值。当 useEffect 内部的回调被调用时,answer 方法将封闭在 setState 之前的状态值上。

这是你将使用它来调用 setTimeout 的函数,因此即使存在超时,旧的状态值也会被延迟。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接