意外状态更新导致“useState”函数

Posted

技术标签:

【中文标题】意外状态更新导致“useState”函数【英文标题】:Unexpected state update results in "useState" function 【发布时间】:2021-12-20 18:06:53 【问题描述】:

考虑这个使用函数更新useState 对象的简单示例:

App.tsx

import  useState  from 'react';

export default function App() 

  const [state, setState] = useState(
    id: 0,
  );

  const update = () => 

    setState(prev => 
      const id = Date.now();
      console.log('previous id:', prev.id, 'new id:', id);
      return 
        id,
      ;
    );

  ;

  return (
    <div>
      <div>state: JSON.stringify(state)</div>
      <button onClick=update>Update</button>
    </div>
  );

将其放入标准的 create-react-app typescript 模板(使用 React 17.0.2),并在开发模式下运行应用程序。继续单击更新按钮并观察控制台。很快就会遇到预期状态和实际状态之间的差异,例如

previous id:  1636301326090  new id:  1636301326260
previous id:  1636301326260  new id:  1636301326440
previous id:  1636301326440  new id: *1636301326611*
previous id: *1636301326612* new id:  1636301326804  // What???
previous id:  1636301326804  new id:  1636301326997

在上面的控制台日志中,id: 1636301326612 状态突然出现,因为设置的状态是 id: 1636301326611

更奇怪的是,当应用程序是为生产而构建时,我无法重现这一点。此外,如果我放弃功能更新并仅将对象传递给setState,就像注释掉的代码一样(由于原子性要求,在我的实际非玩具代码中不可行),问题似乎也不存在。

我完全不知所措。我是否以某种方式使用了setState 错误?

如有必要,我可以提供完整的示例 repo。

【问题讨论】:

【参考方案1】:

这可能是因为在开发中,setState 在严格模式下被调用了两次。 设置状态值不取决于先前的值,而是取决于时间。这是副作用。

相关链接https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects https://github.com/facebook/react/issues/12856

【讨论】:

太好了,所以基本上在下一个状态设置新日期是React 试图在strict-mode 中捕获的杂质,谢谢。 谢谢!我实际上发现这是严格模式双重调用该方法(计划自己提交答案但太忙),但文档链接非常有帮助。我已经阅读了该页面,但不知何故错过了这个非常重要的部分,facepalm。如果不是抑制console.log,而是将其修改为打印“从严格模式双重调用函数:...”,这可能会更有帮助。

以上是关于意外状态更新导致“useState”函数的主要内容,如果未能解决你的问题,请参考以下文章

useState 更新状态值,但将旧值作为参数传递给函数

React useState导致渲染循环

如何立即更新 react useState?

React useState() 使用指南

它们是使用包含更新的道具和先前状态的 useState 钩子来更新状态的功能形式吗?

从另一个屏幕调用函数时 useState 数组未更新