React Navigation 6登出后登录屏幕重复渲染两次问题求助
问题现象
- 应用内执行登出操作时,出现登录屏幕重复渲染两次的异常
- 触发规律:仅在登录后导航到其他页面、再返回初始路由的场景下触发;登录后未离开初始路由直接登出时,登录页正常仅渲染一次
- 技术背景:项目使用Redux结合AsyncStorage实现认证流程管控,使用React Navigation 6(该版本已移除旧版SwitchNavigator,旧版替换导航器的方案无法适用)
关联代码
Redux Reducer(user.js)
import {UPDATE_USER} from '../actions/actionTypes'; const initialState = { userdata: {}, loginStatus: -1, // -1 == loading, 0 = not logged in, 1 == logged in }; const updateUser = (state, action) => { let ud = action.userdata; return {...state, userdata: Object.assign({}, ud), loginStatus: ud ? 1 : 0}; }; const userReducer = (state = initialState, action) => { switch (action.type) { case UPDATE_USER: return updateUser(state, action); default: return state; } }; export default userReducer;
根加载组件(LoadingScreen.js)
import React, {useEffect, useState, useRef} from 'react'; //ThirdParty //import CustomSplashScreen from 'react-native-splash-screen'; import AsyncStorage from '@react-native-async-storage/async-storage'; import {NavigationContainer} from '@react-navigation/native'; //Redux import {updateUser} from '../../redux/actions/userActions'; import {useSelector} from 'react-redux'; import {useDispatch} from 'react-redux'; //Import STACK NAVIGATORS import AuthNavigator from '../../navigator/AuthNavigator'; import MainNavigator from '../../navigator/MainNavigator'; function LoadingScreen() { const ref = useRef(); const loginStatus = useSelector((state) => state.user.loginStatus); const dispatch = useDispatch(); useEffect(() => { console.log('loginStatus', loginStatus); async function getAsyncData() { let data = await AsyncStorage.getItem('data'); let dataParsed = JSON.parse(data); console.log("AsyncData: "+data); //Update redux state dispatch(updateUser(dataParsed)); } getAsyncData(); }); if (loginStatus === -1) { // We haven't finished checking for the user logged in or not return <View style={styles.container} />; } ref && ref.current && ref.current.animateNextTransition(); return ( <NavigationContainer> <Transitioning.View ref={ref} transition={TRANSITION} style={styles.transitionContainer}> {loginStatus === 1 && <MainNavigator />} // STACK NAVIGATOR WHEN SIGNED IN {loginStatus === 0 && <AuthNavigator />} // STACK NAVIGATOR WHEN SIGNED OUT </Transitioning.View> </NavigationContainer> ); } export default LoadingScreen;
登出方法实现
const signOutAsync = async () => { console.log("Begin Logout"); dispatch(updateUser(null)); await AsyncStorage.clear(); };
问题根因
代码里存在两个核心问题共同导致重复渲染:
- 根组件的
useEffect未传入依赖数组,按照React规则,不传第二个参数的effect会在组件每次重渲染时都执行。也就是说每次登录状态变化、组件重渲染,都会重新读取一次AsyncStorage的用户数据、重复触发updateUser的dispatch动作。 - 登出逻辑顺序错误:先更新Redux登录状态为未登录,再异步清除本地存储。两个操作之间存在时间差:状态更新后组件立刻重渲染,触发无依赖的effect读取AsyncStorage,此时存储清除操作还没完成,会读到之前存储的有效用户信息,再次dispatch把登录状态改回已登录,等存储清除完成后又会触发状态变更,最终导致登录页重复渲染。登录后未导航过的场景下导航树状态简单,没有触发重渲染的间隙,所以不会复现问题。
修复方案
1. 修正useEffect依赖,避免重复执行初始化逻辑
给读取本地存储的useEffect传入空依赖数组,让该逻辑仅在组件首次挂载时执行一次,匹配“启动时读取本地登录态”的设计目的:
useEffect(() => { console.log('loginStatus', loginStatus); async function getAsyncData() { let data = await AsyncStorage.getItem('data'); let dataParsed = JSON.parse(data); console.log("AsyncData: "+data); dispatch(updateUser(dataParsed)); } getAsyncData(); }, []); // 新增空依赖数组
react-redux中dispatch的引用是稳定的,不需要加入依赖数组。
2. 调整登出逻辑顺序,消除状态更新间隙
把清除本地存储的操作放到Redux状态更新之前,保证存储操作完成后再切换全局登录状态,避免中间状态触发异常:
const signOutAsync = async () => { console.log("Begin Logout"); await AsyncStorage.clear(); // 先清除本地存储 dispatch(updateUser(null)); // 存储清空后再更新全局登录状态 };
3. (可选优化)明确导航树key,避免React复用组件状态
给两个条件渲染的导航器设置独立key,帮助React明确区分两个独立的导航树,避免状态复用导致的渲染异常:
<Transitioning.View ref={ref} transition={TRANSITION} style={styles.transitionContainer}> {loginStatus === 1 ? <MainNavigator key="main-app" /> : <AuthNavigator key="auth-flow" /> } </Transitioning.View>
React Navigation 6本身已经支持通过条件渲染实现认证流,不需要依赖已移除的SwitchNavigator,以上修复完成后即可解决登录页重复渲染的问题。
内容的提问来源于stack exchange,提问作者Quantal
相关产品推荐
相关产品推荐

