经过一番尝试,我发现严格模式下会出现以下问题。 如果有人能解释原因,我会很感兴趣。
以这个简单的例子为例,在渲染内部我只是安排一个更新状态的超时:
示例:
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
在第一次渲染中设置,并且其值被丢弃(另请参阅答案)。
这是因为严格模式故意调用函数组件主体两次(在开发模式下)以帮助发现意外的副作用。
在第二次调用时,您的
firstRender
变量是 false
,因此您的 setTimeout 不会运行。
重要的是要注意,第二次调用不仅仅是像从状态更新中获得的那样重新渲染。这是整个组件主体的第二次调用。状态未保留。 React 调用您的组件函数一次,丢弃结果,然后第二次调用它以获取输出。
来自文档:
因为上述方法可能会被多次调用,所以它们不包含副作用很重要。
严格模式无法自动为您检测副作用,但它可以通过使副作用更具确定性来帮助您发现它们。这是通过有意双重调用以下函数来完成的:
- 功能组件体
问。似乎使用 useRef 而不是全局变量firstRender 在严格模式下也修复了此问题。好奇为什么会这样?
根据React文档,
Ref 对于保留一些可变值也很有用。 存储在 ref 中的值在后续重新渲染期间不会更改,除非显式更改。
因此,对于您不想更改的值(可能是一些昂贵的计算)或出于其他原因,请使用
useRef
。
“ref”对象是一个通用容器,其当前属性是 可变的并且可以保存任何值,类似于实例属性 类。
这是有效的,因为 useRef() 创建了一个普通的 JavaScript 对象。这 useRef() 和创建 {current: ...} 对象之间的唯一区别 你自己是 useRef 会给你相同的 ref 对象 渲染。
请记住,useRef 的内容更改时不会通知您。 改变 .current 属性不会导致重新渲染。如果你想 当 React 将 ref 附加或分离到 DOM 节点时运行一些代码, 您可能想改用回调引用。