React导航求助:未受保护路由Stack与受保护路由Drawer实现问题
问题描述
作为React Navigation新手,尝试实现未受保护路由使用Stack Navigator、受保护路由使用Drawer Navigator,但出现两个核心问题:
- 登录后调用
navigation.navigate('Login')提示找不到该路由; - 即使token为null,应用仍默认跳转至Home页面。
用户原实现代码如下:
AppNav.js
// Navigation Components import DrawerNavigator from './DrawerNavigator'; import StackNavigator from './StackNavigator'; const AppNav = () => { const [Auth, setAuth] = useState(null); useEffect(() => { const checkAuth = async () => { const token = await getToken(); console.log('token AppNav: ', token); if (token) { setAuth(true); } else { setAuth(false); } }; checkAuth(); }, []); const Routes = [ { name: 'Home', component: Home, isAuth: true, }, { name: 'Login', component: Login, isAuth: false, }, { name: 'OTPScreen', component: OTPScreen, isAuth: false, }, { name: 'ResetPassword', component: ResetPassword, isAuth: false, }, { name: 'SignUp', component: SignUp, isAuth: false, }, ]; return ( <NavigationContainer> {Auth ? <DrawerNavigator routes={Routes} /> : <StackNavigator routes={Routes} />} </NavigationContainer> ); }; export default AppNav;
DrawerNavigator.js
import React from 'react'; import { createDrawerNavigator } from '@react-navigation/drawer'; import PropTypes from 'prop-types'; // Utils import { uniqueId } from '../common/utils'; const Drawer = createDrawerNavigator(); const DrawerNavigator = ({ routes }) => { const authRoutes = routes.filter((route) => route.isAuth); return ( <Drawer.Navigator screenOptions={{ headerShown: false }}> {authRoutes.map((route) => ( <Drawer.Screen key={uniqueId('drawer')} name={route.name} component={route.component} /> ))} </Drawer.Navigator> ); }; DrawerNavigator.propTypes = { routes: PropTypes.arrayOf(PropTypes.object).isRequired, }; export default DrawerNavigator;
StackNavigator.js
import React from 'react'; import PropTypes from 'prop-types'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; // Utils import { uniqueId } from '../common/utils'; const Stack = createNativeStackNavigator(); const StackNavigator = ({ routes }) => ( <Stack.Navigator screenOptions={{ headerShown: false }}> {routes.map((route) => ( <Stack.Screen key={uniqueId('navigator')} name={route.name} component={route.component} /> ))} </Stack.Navigator> ); StackNavigator.propTypes = { routes: PropTypes.arrayOf(PropTypes.object).isRequired, }; export default StackNavigator;
问题原因分析
- 路由范围不清晰:登录后切换到Drawer Navigator,但Drawer仅注册了
isAuth: true的路由(仅Home),因此找不到Login路由; - 初始状态逻辑错误:
Auth初始值为null,异步校验token完成前,Stack Navigator会渲染所有路由(包括Home),而Stack默认显示数组第一个路由(Home),导致未登录时先显示Home; - 路由注册冗余:未登录时的Stack包含受保护路由Home,存在安全隐患。
修正后的实现方案
1. 拆分路由配置
将公共路由(未受保护)和私有路由(受保护)分开定义:
// 未受保护路由(Stack专用) const PublicRoutes = [ { name: 'Login', component: Login }, { name: 'OTPScreen', component: OTPScreen }, { name: 'ResetPassword', component: ResetPassword }, { name: 'SignUp', component: SignUp }, ]; // 受保护路由(Drawer专用) const PrivateRoutes = [ { name: 'Home', component: Home }, // 可添加其他受保护路由,如Profile、Settings等 ];
2. 优化AppNav认证状态处理
添加加载状态、明确初始路由、支持登录/登出状态更新:
import React, { useState, useEffect } from 'react'; import { NavigationContainer } from '@react-navigation/native'; import DrawerNavigator from './DrawerNavigator'; import StackNavigator from './StackNavigator'; import { ActivityIndicator, View } from 'react-native'; const PublicRoutes = [ { name: 'Login', component: Login }, { name: 'OTPScreen', component: OTPScreen }, { name: 'ResetPassword', component: ResetPassword }, { name: 'SignUp', component: SignUp }, ]; const PrivateRoutes = [ { name: 'Home', component: Home }, ]; const AppNav = () => { const [isAuthenticated, setIsAuthenticated] = useState(undefined); // 初始未校验状态 useEffect(() => { checkAuthStatus(); }, []); const checkAuthStatus = async () => { const token = await getToken(); setIsAuthenticated(!!token); // 转换为布尔值 }; // 供登录/登出组件调用的状态更新方法(可通过Context传递) const updateAuthStatus = (status) => { setIsAuthenticated(status); }; // 校验完成前显示加载动画 if (isAuthenticated === undefined) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <ActivityIndicator size="large" /> </View> ); } return ( <NavigationContainer> {isAuthenticated ? ( <DrawerNavigator routes={PrivateRoutes} /> ) : ( <StackNavigator routes={PublicRoutes} /> )} </NavigationContainer> ); }; export default AppNav;
3. 修正StackNavigator与DrawerNavigator
- StackNavigator设置默认初始路由为Login,避免未登录时显示Home;
- 用路由名称作为key,替代不稳定的uniqueId:
StackNavigator.js
import React from 'react'; import PropTypes from 'prop-types'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); const StackNavigator = ({ routes }) => ( <Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName="Login" // 默认显示登录页 > {routes.map((route) => ( <Stack.Screen key={route.name} name={route.name} component={route.component} /> ))} </Stack.Navigator> ); StackNavigator.propTypes = { routes: PropTypes.arrayOf(PropTypes.object).isRequired, }; export default StackNavigator;
DrawerNavigator.js
import React from 'react'; import { createDrawerNavigator } from '@react-navigation/drawer'; import PropTypes from 'prop-types'; const Drawer = createDrawerNavigator(); const DrawerNavigator = ({ routes }) => { return ( <Drawer.Navigator screenOptions={{ headerShown: false }}> {routes.map((route) => ( <Drawer.Screen key={route.name} name={route.name} component={route.component} /> ))} </Drawer.Navigator> ); }; DrawerNavigator.propTypes = { routes: PropTypes.arrayOf(PropTypes.object).isRequired, }; export default DrawerNavigator;
4. 登录/登出逻辑示例
- 登录成功后更新认证状态:
// Login组件内 const handleLogin = async () => { await saveToken('your-token'); // 存储token updateAuthStatus(true); // 切换到Drawer路由 };
- 登出时重置路由并更新状态:
// Home组件内 const handleLogout = async () => { await removeToken(); // 清除token navigation.reset({ index: 0, routes: [{ name: 'Login' }], }); updateAuthStatus(false); // 切换到Stack路由 };
关键优化点
- 拆分路由范围,避免未登录时访问受保护路由;
- 添加加载状态,解决初始渲染错误路由的问题;
- 明确Stack默认初始路由,确保未登录时直接显示登录页;
- 支持主动更新认证状态,确保路由即时切换;
- 使用路由名称作为key,提升组件渲染稳定性。
内容的提问来源于stack exchange,提问作者Faziki
相关产品推荐
相关产品推荐

