React Native登出报错:无法在已卸载组件上执行状态更新
修复React移动端App登出后的内存泄漏问题
问题根源
你遇到的Can't perform a React state update on an unmounted component警告,核心原因是:
- App.js在组件渲染流程中直接发起异步请求:
if(!loaded)之后的CheckingSession().then()会在组件每次渲染时执行,登出后组件卸载过程中,异步请求完成后调用setUserLoggedIn,就会触发内存泄漏警告。 - 重复调用异步检查:useEffect和组件渲染阶段两次调用
CheckingSession,冗余且加剧了状态更新冲突。
具体修复步骤
1. 移除组件渲染阶段的异步调用
直接删除App.js中if(!loaded)之后的这段代码:
CheckingSession().then((r) => { setUserLoggedIn(r) })
2. 优化useEffect的异步逻辑,添加卸载清理
修改App.js中的useEffect,加上清理函数确保组件卸载后不再更新状态,同时整合异步操作:
useEffect(() => { isMounted.current = true; const fetchAuthAndSession = async () => { // 先检查auth状态 const authResult = await CheckingAuth(); // 再检查session状态 const sessionResult = await CheckingSession(); if (isMounted.current) { setRefreshPage(prev => prev + 1); setUserLoggedIn(sessionResult); } }; fetchAuthAndSession(); // 组件卸载时标记为未挂载 return () => { isMounted.current = false; }; }, []);
3. 修正Profil.js中的异步操作(避免状态不同步)
AsyncStorage.setItem是异步操作,需要加await确保状态设置完成后再刷新App:
const Loggout = async () => { await AsyncStorage.setItem("@LoggedIn", "false"); ToastMsgNotification.CustomSuccess("Déconnexion", "Vous avez été déconnecté"); route.params.refreshApp(); };
修改后的完整App.js代码
async function CheckingSession() { let UserLoggedIn = await AsyncStorage.getItem("@LoggedIn"); return UserLoggedIn; } async function CheckingAuth() { let UserToken = await AsyncStorage.getItem('@JWT') let UserLoggedIn = await AsyncStorage.getItem('@LoggedIn'); if (UserLoggedIn != "true" && UserToken == null) return false; if (UserToken) { try { const answer = await axiosInstance.post('/api/login/check_jwt', UserToken, { headers: { 'Authorization': UserToken, 'Accept' : 'application/json', 'Content-Type': 'application/json' } }); if (answer.data.JWT === "OK") return true; } catch (err) { // 捕获请求失败的情况,避免崩溃 console.error('Token check failed:', err); } ToastMsgNotification.CustomError("Accès expiré", "Merci de vous reconnecter à votre compte"); return false; } return false; // 补充默认返回值 } export default function App(props) { const [refreshPage, setRefreshPage] = React.useState(0); const [loaded] = useFonts({ MontserratM: require('../../../assets/fonts/Montserrat-Medium.ttf'), MontserratB: require('../../../assets/fonts/Montserrat-Bold.ttf'), MontserratSB: require('../../../assets/fonts/Montserrat-SemiBold.ttf'), MontserratR: require('../../../assets/fonts/Montserrat-Regular.ttf'), MontserratL: require('../../../assets/fonts/Montserrat-Light.ttf'), MontserratXB: require('../../../assets/fonts/Montserrat-ExtraBold.ttf'), }); const ref = React.useRef(null); const [userLoggedIn, setUserLoggedIn] = React.useState(false); const isMounted = React.useRef(false); useEffect(() => { isMounted.current = true; const fetchAuthAndSession = async () => { const authResult = await CheckingAuth(); const sessionResult = await CheckingSession(); if (isMounted.current) { setRefreshPage(prev => prev + 1); setUserLoggedIn(sessionResult); } }; fetchAuthAndSession(); return () => { isMounted.current = false; }; }, []); if (!loaded) { return null; } return ( <NavigationContainer ref={ref}> <StatusBar style="dark" /> { userLoggedIn === "true" ? (<NavigationLoggedIn refreshApp={() => setRefreshPage(prev => prev + 1)} />) : (<NavigationNotLoggedIn refreshApp={() => setRefreshPage(prev => prev + 1)} />) } <Toast config={toastConfig} style={{position: 'absolute'}}/> </NavigationContainer> ) }
额外优化说明
- 给
CheckingAuth添加了try/catch捕获请求异常,避免网络错误导致组件崩溃。 - 状态更新时使用函数式更新(
setRefreshPage(prev => prev + 1)),确保获取最新的状态值,避免闭包问题。 - 给
CheckingAuth补充了默认返回值,避免逻辑分支遗漏导致的undefined返回。
内容的提问来源于stack exchange,提问作者BigSunNoSeum
相关产品推荐
相关产品推荐

