【Bug解决】Can‘t perform a React state update on an unmounted component. This is > a no-op, but it...
在 React 应用程序中我们遇到以下警告消息:
Can’t perform a React state update on an unmounted component. This is
a no-op, but it indicates a memory leak in your application. To fix,
cancel all subscriptions and asynchronous tasks in a useEffect cleanup
function.
这是因为我们在使用异步调用时,造成了内存泄漏。
为什么异步调用可能会内存泄漏?
如果在卸载组件后更新状态,执行状态更新和运行异步操作的 React 组件就会导致内存泄漏问题,比如:
- 用户执行触发事件处理程序以从 API 获取数据的操作。
- 然后,用户点击一个链接,在完成第 1 步之前导航到另一个页面。
- 此时,第1步操作完成并传递从 API 获取到的数据并调用setState。
由于组件已卸载,并且函数正在已经卸载的组件中调用,因此会导致内存泄漏问题——并且在控制台中,会出现上述警告。
错误代码示例:
const [value, setValue] = useState('checking value...');
useEffect(() => {
fetchValue().then(() => {
setValue("done!"); // ⚠️ what if the component is no longer mounted ?
// we got console warning of memory leak
});
}, []);
如何避免此类问题
1、 使用变量标记组件状态
const [value, setValue] = useState('checking value...');
useEffect(() => {
let isMounted = true;
fetchValue().then(() => {
if(isMounted ){
setValue("done!"); // no more error
}
});
return () => {
isMounted = false;
};
}, []);
在上面的代码中,创建了一个变量isMounted
,其初始值为true
。当isMounted
为true
时,更新状态并返回函数。否则,如果操作在完成之前被卸载,则函数isMounted以 false
返回。
2、使用 AbortController
useEffect(() => {
let abortController = new AbortController();
// your async action is here
return () => {
abortController.abort();
}
}, []);
- 在上述代码中,
AbortController
用来实现取消订阅效果。异步操作完成后,中止控制器取消订阅。
3、使用use-state-if-mounted Hook
const [value, setValue] = useStateIfMounted('checking value...');
useEffect(() => {
fetchValue().then(() => {
setValue("done!"); // no more error
});
}, []);
在上述代码中,使用了一个自定义的钩子 useStateIfMounted
,它的作用用来在更新状态之前检查组件是否已安装!