我有一个计数器和一个useEffect
中的console.log()
来记录我的状态中的每一个变化,但是useEffect
在挂载时被调用了两次。我使用的是React 18。下面是我的项目的一个CodeSandbox和下面的代码:
import { useState, useEffect } from "react";
const Counter = () => {
const [count, setCount] = useState(5);
useEffect(() => {
console.log("rendered", count);
}, [count]);
return (
<div>
<h1> Counter </h1>
<div> {count} </div>
<button onClick={() => setCount(count + 1)}> click to increase </button>
</div>
);
};
export default Counter;
3条答案
按热度按时间6rvt4ljy1#
这是自React 18以来的正常行为,当你在
development
中使用StrictMode
时。以下是他们在文档中所说的概述:在未来,我们希望添加一个特性,允许React添加和删除UI的部分,同时保持状态。例如,当用户从一个屏幕切换到另一个屏幕时,React应该能够立即显示上一个屏幕。为此,React将支持使用卸载前使用的相同组件状态重新安装树。
此功能将为React提供更好的开箱即用性能,但要求组件对多次挂载和销毁的效果具有弹性。大多数效果无需任何更改即可工作,但某些效果无法在销毁回调中正确清除订阅,或隐式假定它们仅被挂载或销毁一次。
为了帮助解决这些问题,React 18引入了一个新的仅用于开发的严格模式检查。每当组件第一次装载时,这种新的检查将自动卸载并重新装载每个组件,并在第二次装载时恢复先前的状态。
这仅适用于
development
模式,production
行为不变。这看起来很奇怪,但最终,它是为了让你编写出更好的React代码,没有错误,符合当前的指导方针,并与未来的版本兼容,通过使用React Query这样的库缓存HTTP请求,并在有两个调用的问题时使用cleanup函数。
在这篇名为Synchronizing with Effects的详细文章中,React团队以前所未有的方式解释了
useEffect
,并举例说:这说明,如果重新挂载破坏了应用程序的逻辑,通常会发现现有的bug。从用户的Angular 来看,访问一个页面应该与访问该页面、单击链接然后按“返回”没有什么不同。React验证了组件在开发过程中重新挂载一次不会破坏这一原则。
对于您的特定用例,您可以让它保持原样而不用担心。而且您不应该尝试在
useEffect
中使用那些带有useRef
和if
语句的技术来使它触发一次,或者删除StrictMode
,如下所示:React会在开发过程中有意地重新挂载你的组件,以帮助你找到bug。正确的问题不是“如何运行一次效果”,而是“如何修复我的效果,使其在重新挂载后仍能工作”。
通常,答案是实现清理功能。清理功能应该停止或撤销效果正在做的任何事情。经验法则是,用户应该无法区分效果运行一次(如在生产中)和设置→清理→设置序列(如在开发中所见)。
dgiusagp2#
**更新:**回头看这篇帖子,稍微明智一点,请不要这样做。
使用
ref
或自定义不带ref
的hook
。isr3a4wc3#
(对已接受答案的一个小补充)
仅在第一次装载时应用效果:
仅在第二个坐骑上应用效果: