严格模式下状态不会更新

问题描述 投票:0回答:2

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

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

示例

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>
  );
}

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

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

注意:即使在严格模式下,如果使用

useRef
而不是全局变量
firstRender
,也不会重复此行为。发生这种情况似乎是因为
ref
在第一次渲染中设置,并且其值被丢弃(另请参阅答案)。

javascript reactjs react-hooks
2个回答
8
投票

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

在第二次调用时,您的

firstRender
变量是
false
,因此您的 setTimeout 不会运行。

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

来自文档

因为上述方法可能会被多次调用,所以它们不包含副作用很重要。

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

  • 功能组件体

-1
投票

问。似乎使用 useRef 而不是全局变量firstRender 在严格模式下也修复了此问题。好奇为什么会这样?

根据React文档

Ref 对于保留一些可变值也很有用。 存储在 ref 中的值在后续重新渲染期间不会更改,除非显式更改。

因此,对于您不想更改的值(可能是一些昂贵的计算)或出于其他原因,请使用

useRef

“ref”对象是一个通用容器,其当前属性是 可变的并且可以保存任何值,类似于实例属性 类。

这是有效的,因为 useRef() 创建了一个普通的 JavaScript 对象。这 useRef() 和创建 {current: ...} 对象之间的唯一区别 你自己是 useRef 会给你相同的 ref 对象 渲染。

请记住,useRef 的内容更改时不会通知您。 改变 .current 属性不会导致重新渲染。如果你想 当 React 将 ref 附加或分离到 DOM 节点时运行一些代码, 您可能想改用回调引用。

© www.soinside.com 2019 - 2024. All rights reserved.