在 StrictMode 下状态不会更新

gio*_*gim 7 javascript reactjs react-hooks

经过一番尝试,我发现严格模式下会出现以下问题。如果有人能解释原因,我会很感兴趣。

以这个简单的例子为例,在渲染内部我只是安排一个更新状态的超时

例子

let firstRender = true; // Normally I would use ref but I was playing with example

export default function App() {
  let [data, setData] = React.useState({ name: 'Nick' });

  // Schedule a timeout on first render
  if (firstRender) {
    setTimeout(() => {
      console.log('Running');

      setData((ps) => ({
        ...ps,
        name: 'Paul',
      }));
    }, 1000);
  }

  console.log('Running render');
  firstRender = false;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>Start editing to see some magic happen :)</p>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

如果您在没有严格模式的情况下运行此示例,那么一秒钟后您将在屏幕上看到“Paul”,正如我所期望的那样。

如果您使用严格模式,屏幕上将始终显示“Nick”。知道为什么吗?

注意:似乎使用useRef而不是全局变量firstRender也可以在严格模式下修复此问题。发生这种情况似乎是因为ref在第一次渲染中设置了它,并且它的值也被丢弃了(另请参阅答案)。

ray*_*eld 7

这是因为严格模式故意调用函数组件主体两次(在开发模式下)以帮助发现意外的副作用。

\n

在第二次调用时,您的firstRender变量是false这样的,因此您的 setTimeout 不会运行。

\n

需要注意的是,第二次调用不仅仅是像状态更新那样的重新渲染。这是整个组件主体的第二次调用。状态未保留。React 调用组件函数一次,丢弃结果,然后第二次调用它以获取输出。

\n

来自文档

\n
\n

由于上述方法可能会被多次调用,因此重要的是它们不包含副作用。

\n
\n
\n

严格模式可以\xe2\x80\x99t自动为您检测副作用,但它可以通过使副作用更具确定性来帮助您发现它们。这是通过有意两次调用以下函数来完成的:

\n
\n
\n
    \n
  • 功能组件体
  • \n
\n
\n