这仅适用于development 模式,production 行为不变。
这看起来很奇怪,但最后,它就在那里,所以你可以编写更好的 React 代码,其中每个 useEffect 都有其 clean up 函数,只要有两个调用是一个问题。这里有两个例子:
/* Having a setInterval inside an useEffect: */
import { useEffect, useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => setCount((count) => count + 1), 1000);
/*
Make sure I clear the interval when the component is unmounted,
otherwise I get weird behaviour with StrictMode,
helps prevent memory leak issues.
*/
return () => clearInterval(id);
}, []);
return <div>{count}</div>;
};
export default Counter;
/* An API call inside an useEffect with fetch, almost similar with axios: */
useEffect(() => {
const abortController = new AbortController();
const fetchUser = async () => {
try {
const res = await fetch("/api/user/", {
signal: abortController.signal,
});
const data = await res.json();
} catch (error) {
if (error.name === "AbortError") {
/*
Most of the time there is nothing to do here
as the component is unmounted.
*/
} else {
/* Logic for other cases like request failing goes here. */
}
}
};
fetchUser();
/*
Abort the request as it isn't needed anymore, the component being
unmounted. Helps avoid among other things the well known "can't
perform a React state update on an unmounted component" waring.
*/
return () => abortController.abort();
}, []);
在这篇名为Synchronizing with Effects 的非常详细的文章中,React 团队以前所未有的方式解释了useEffect,并举了一个例子:
这说明如果重新挂载会破坏应用程序的逻辑,这通常会发现现有的错误.从用户的角度来看,访问一个页面应该与访问它、单击一个链接然后按返回没有什么不同。
React 通过在开发中重新安装组件来验证您的组件不会违反此原则。
对于您的特定用例,您可以不用担心。但是,如果您需要,说您希望 useEffect 的回调仅在 count 更改时运行,您可以使用 boolean 和 useRef 添加一些额外的控件,如下所示:
import { useEffect, useRef, useState } from "react";
const Counter = () => {
const countHasChangedRef = useRef(false);
const [count, setCount] = useState(5);
useEffect(() => {
if (!countHasChangedRef.current) return;
console.log("rendered", count);
}, [count]);
return (
<div>
<h1>Counter</h1>
<div>{count}</div>
<button
onClick={() => {
setCount(count + 1);
countHasChangedRef.current = true;
}}
>
Click to increase
</button>
</div>
);
};
export default Counter;
最后,如果您根本不想处理这种development 行为,您可以删除将App 包装在index.js 或index.tsx 中的StrictMode 组件。对于Next.js,删除reactStrictMode: true 内的next.config.js。
然而StrictMode 是在development 期间突出潜在问题的工具。并且通常总是有推荐的解决方法,而不是删除它。