You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native登出报错:无法在已卸载组件上执行状态更新

修复React移动端App登出后的内存泄漏问题

问题根源

你遇到的Can't perform a React state update on an unmounted component警告,核心原因是:

  1. App.js在组件渲染流程中直接发起异步请求:if(!loaded)之后的CheckingSession().then()会在组件每次渲染时执行,登出后组件卸载过程中,异步请求完成后调用setUserLoggedIn,就会触发内存泄漏警告。
  2. 重复调用异步检查: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 14:02:54