"useState" 函数中的意外状态更新结果

Unexpected state update results in "useState" function

考虑这个简单的例子,其中一个函数用于更新 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

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

可能是开发中严格模式调用了两次setState所致
设置状态值不依赖于之前的值,而是依赖于时间。这是副作用。

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