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

React导航求助:未受保护路由Stack与受保护路由Drawer实现问题

React Navigation:未受保护路由用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;

问题原因分析

  1. 路由范围不清晰:登录后切换到Drawer Navigator,但Drawer仅注册了isAuth: true的路由(仅Home),因此找不到Login路由;
  2. 初始状态逻辑错误:Auth初始值为null,异步校验token完成前,Stack Navigator会渲染所有路由(包括Home),而Stack默认显示数组第一个路由(Home),导致未登录时先显示Home;
  3. 路由注册冗余:未登录时的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:53:21