[英]Can't perform a React state update on an unmounted component with useEffect hook
[英]UseEffect hook error, Can't perform a React state update on an unmounted component
当 appState 处于活动状态时,我试图在我的 useEffect 挂钩中设置 state,但是,我收到了警告。 当我的应用程序处于活动状态时,如何设置 state?
警告:无法对未安装的组件执行 React state 更新。 这是一个空操作,但它表明您的应用程序中存在 memory 泄漏。 要修复,请取消 useEffect 清理中的所有订阅和异步任务 function。
useEffect(() => {
const subscription = AppState.addEventListener(
"change",
async (nextAppState) => {
if (
appState.current.match(/inactive|background/) &&
nextAppState === "active"
) {
setAppStateVisible(true); // set state to true
}
appState.current = nextAppState;
setAppStateVisible(appState.current);
}
);
return () => {
subscription.remove();
};
}, []);
是的,这是 React 中的一个常见问题,目前还没有官方解决方案(AFAIK)。
你可以做这样的事情(它有效,但一半的 Inte.net 认为这是反模式):
useEffect(() => {
let isMounted = true;
const subscription = AppState.addEventListener("change",
async (nextAppState) => {
if (appState.current.match(/inactive|background/) && nextAppState === "active" && isMounted) {
setAppStateVisible(true); // set state to true
}
appState.current = nextAppState;
if(isMounted) {
setAppStateVisible(appState.current);
}
});
return () => {
isMounted = false;
subscription.remove();
};
}, []);
基本上,这个想法是将您的 setAppStateVisible 调用包装在一个保护性检查中,检查该组件是否已安装 - 因为如果组件已卸载,您不应该更新该组件的 state(如消息所述)。
由于您在清理回调中删除(取消)您的订阅,我猜您的async
function 中可能有一个或多个await
s(否则,为什么要将其设置为async
function?),尽管您没有显示任何内容。 由于await
s 意味着您的代码暂停然后稍后再次继续(当 promise 它正在await
解决时),您需要考虑到您的组件在此期间被卸载的可能性。
理想情况下, subscription
可以让您检查它是否已被删除(如AbortSignal
的aborted
标志)。 不过,我找不到它的任何文档;AppState.addEventListener
说它是一个EventSubscription
,但没有提供链接,而且我找不到EventSubscription
的任何文档。
如果它有一个,你想使用它。 否则,除了删除订阅的代码之外,您还可以添加一个简单的标志:
useEffect(() => {
let cancelled = false; // ***
const subscription = AppState.addEventListener(
"change",
async (nextAppState) => {
if (cancelled) { //
return; // ***
} //
if (
appState.current.match(/inactive|background/) &&
nextAppState === "active"
) {
setAppStateVisible(true); // set state to true
}
await something(); // Example of an `await`
if (cancelled) { //
return; // ***
} //
appState.current = nextAppState;
setAppStateVisible(appState.current);
}
);
return () => {
cancelled = true; // ***
subscription.remove();
};
}, []);
如果你没有任何await
,那么 React Native 似乎有一些竞争条件,即使你已经对它进行了remove
,它仍然可能调用你的订阅回调,这不太理想。 上面也会处理这个问题。
单独使用这样的cancelled
标志通常被认为是一种反模式(取消订阅比忽略调用更好),但这不是我们上面所做的。 我们正在取消订阅并处理取消订阅后代码被调用或继续的可能性,这很好。
如果你在EventSubscription
上找不到标志,你可以考虑给自己一个包装器,这样你就可以轻松地重用它:
function subscribeAppState(fn) {
const subscription = AppState.subscribe(fn);
return {
remove() {
subscription.remove();
this.removed = true;
}
};
}
然后:
useEffect(() => {
const sub = subscribeAppState(
"change",
async (nextAppState) => {
if (sub.removed) { //
return; // ***
} //
if (
appState.current.match(/inactive|background/) &&
nextAppState === "active"
) {
setAppStateVisible(true); // set state to true
}
await something(); // Example of an `await`
if (sub.removed) { //
return; // ***
} //
appState.current = nextAppState;
setAppStateVisible(appState.current);
}
);
return () => {
sub.remove();
};
}, []);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.