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

React Navigation实现多Tab抽屉打开同屏多实例问题求助

React Navigation 实现LinkedIn式抽屉导航交互问题解决方案

问题说明

需要实现类似LinkedIn的交互效果:从抽屉导航点击设置、个人资料等页面时,在当前激活的Tab对应的Stack中打开页面,并支持生成多个页面实例。当前导航结构为Drawer嵌套TabNavigator,每个Tab页都是一个Stack,且所有Stack都配置了相同的页面(如Profile、Settings)。但在自定义Drawer中导航时,React Navigation无法自动确定使用哪个Stack,且无法直接获取当前激活的Stack名称,导致无法动态指定导航目标。

问题代码

App.tsx

/* eslint-disable react-native/no-inline-styles */
import 'react-native-gesture-handler';

import React from 'react';
import { Text } from 'react-native';

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import {
  createDrawerNavigator,
  DrawerContentScrollView,
  DrawerItem,
} from '@react-navigation/drawer';
import { NavigationContainer } from '@react-navigation/native';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { StatusBar } from 'expo-status-bar';

import HomeStackScreen from '@pages/Home';
import { Routes } from '@src/constants/routes';
import { PracticalStackScreen } from '@src/pages/Practical/Practical';
import { TheoryStackScreen } from '@src/pages/Theory/Theory';

// Create a client
const queryClient = new QueryClient();

const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();

const TabNavigator = () => {
  return (
    <Tab.Navigator
      screenOptions={{ headerShown: false }}
      initialRouteName={Routes.student.home.STACK}
    >
      <Tab.Screen
        name={Routes.student.theory.STACK}
        component={TheoryStackScreen}
        options={{ title: 'TEÓRICO' }}
      />
      <Tab.Screen
        name={Routes.student.home.STACK}
        component={HomeStackScreen}
        options={{ title: '' }}
      />
      <Tab.Screen
        name={Routes.student.practical.STACK}
        component={PracticalStackScreen}
        options={{ title: 'PRÁCTICO' }}
      />
    </Tab.Navigator>
  );
};

function CustomDrawerContent(props: any) {
  console.log('props ', props);
  return (
    <DrawerContentScrollView {...props}>
      <Text>Mi cuenta</Text>
      <DrawerItem
        label='Configuración'
        onPress={() => props.navigation.navigate(Routes.student.SETTINGS)}
      />
      <DrawerItem
        label='Métodos de pago'
        onPress={() => props.navigation.navigate(Routes.student.PAYMENT)}
      />
      <Text>Social</Text>
      <DrawerItem
        label='Tiktok'
        onPress={() => props.navigation.navigate(Routes.student.PROFILE)}
      />
      <Text>Ayuda</Text>
      <DrawerItem
        label='Preguntas frecuentes'
        onPress={() => props.navigation.navigate(Routes.student.FAQ)}
      />
      <DrawerItem
        label='Atención al alumno'
        onPress={() => props.navigation.navigate(Routes.student.STUDENT_SUPPORT)}
      />
    </DrawerContentScrollView>
  );
}

const DrawerNavigation = () => {
  return (
    <Drawer.Navigator
      useLegacyImplementation={true}
      drawerContent={(props) => <CustomDrawerContent {...props} />}
      initialRouteName={Routes.student.home.STACK}
    >
      <Drawer.Screen name={Routes.MAIN_TAB} component={TabNavigator} />
    </Drawer.Navigator>
  );
};
export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <StatusBar />
      <NavigationContainer>
        <DrawerNavigation />
      </NavigationContainer>
    </QueryClientProvider>
  );
}

Home.tsx

/* eslint-disable react-native/no-color-literals */
import React from 'react';
import { StyleSheet, Text, View, Pressable } from 'react-native';

import { createNativeStackNavigator } from '@react-navigation/native-stack';

import { Routes } from '@src/constants/routes';

import Faq from '../Faq';
import Payment from '../Payment';
import Profile from '../Profile';
import Settings from '../Settings';
import StudentSupport from '../StudentSupport';

const HomeStack = createNativeStackNavigator();

export const HomeStackScreen = (): JSX.Element => {
  return (
    <HomeStack.Navigator
      screenOptions={{ headerStyle: { backgroundColor: 'red' }, headerShown: false }}
    >
      <HomeStack.Screen name={Routes.student.home.MAIN} component={Home} />
      <HomeStack.Screen name={Routes.student.PROFILE} component={Profile} />
      <HomeStack.Screen name={Routes.student.SETTINGS} component={Settings} />
      <HomeStack.Screen name={Routes.student.PAYMENT} component={Payment} />
      <HomeStack.Screen name={Routes.student.FAQ} component={Faq} />
      <HomeStack.Screen name={Routes.student.STUDENT_SUPPORT} component={StudentSupport} />
    </HomeStack.Navigator>
  );
};

export const Home = ({ navigation }: any): JSX.Element => (
  <View style={styles.container}>
    <Text>Home Screen</Text>
    <Pressable style={styles.button} onPress={() => navigation.navigate(Routes.student.PROFILE)}>
      <Text style={styles.text}>Perfil</Text>
    </Pressable>
  </View>
);

const backgroundColor = '#fff';

const styles = StyleSheet.create({
  button: {
    alignItems: 'center',
    backgroundColor: 'grey',
    borderRadius: 4,
    elevation: 3,
    justifyContent: 'center',
    paddingHorizontal: 32,
    paddingVertical: 12,
  },
  container: {
    alignItems: 'center',
    backgroundColor,
    flex: 1,
    justifyContent: 'center',
  },
  text: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
    letterSpacing: 0.25,
    lineHeight: 21,
  },
});

Theory.tsx

import React from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';

import { createNativeStackNavigator } from '@react-navigation/native-stack';

import { Routes } from '@src/constants/routes';

import Faq from '../Faq';
import Payment from '../Payment';
import Profile from '../Profile';
import Settings from '../Settings';
import StudentSupport from '../StudentSupport';
const TheoryStack = createNativeStackNavigator();

export const TheoryStackScreen = (): JSX.Element => {
  return (
    <TheoryStack.Navigator
      screenOptions={{ headerStyle: { backgroundColor: 'red' }, headerShown: false }}
    >
      <TheoryStack.Screen name={Routes.student.theory.MAIN} component={Theory} />
      <TheoryStack.Screen name={Routes.student.PROFILE} component={Profile} />
      <TheoryStack.Screen name={Routes.student.SETTINGS} component={Settings} />
      <TheoryStack.Screen name={Routes.student.PAYMENT} component={Payment} />
      <TheoryStack.Screen name={Routes.student.FAQ} component={Faq} />
      <TheoryStack.Screen name={Routes.student.STUDENT_SUPPORT} component={StudentSupport} />
    </TheoryStack.Navigator>
  );
};
export function Theory({ navigation }: any): JSX.Element {
  return (
    <View style={styles.container}>
      <Text>Theory Screen</Text>
      <Button title='Go to Home' onPress={() => navigation.navigate(Routes.student.home.STACK)} />
      <Button
        title='Go to Practical'
        onPress={() => navigation.navigate(Routes.student.practical.STACK)}
      />
      <Button
        title='Go to Profile'
        onPress={() =>
          navigation.navigate(Routes.student.home.STACK, { screen: Routes.student.PROFILE })
        }
      />
    </View>
  );
}

const backgroundColor = '#fff';

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    backgroundColor,
    flex: 1,
    justifyContent: 'center',
  },
});

Practical.tsx

import React from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';

import { createNativeStackNavigator } from '@react-navigation/native-stack';

import { Routes } from '@src/constants/routes';

import Faq from '../Faq';
import Payment from '../Payment';
import Profile from '../Profile';
import Settings from '../Settings';
import StudentSupport from '../StudentSupport';
const PracticalStack = createNativeStackNavigator();

export const PracticalStackScreen = (): JSX.Element => {
  return (
    <PracticalStack.Navigator
      screenOptions={{ headerStyle: { backgroundColor: 'red' }, headerShown: false }}
    >
      <PracticalStack.Screen name={Routes.student.practical.MAIN} component={Practical} />
      <PracticalStack.Screen name={Routes.student.PROFILE} component={Profile} />
      <PracticalStack.Screen name={Routes.student.SETTINGS} component={Settings} />
      <PracticalStack.Screen name={Routes.student.PAYMENT} component={Payment} />
      <PracticalStack.Screen name={Routes.student.FAQ} component={Faq} />
      <PracticalStack.Screen name={Routes.student.STUDENT_SUPPORT} component={StudentSupport} />
    </PracticalStack.Navigator>
  );
};

export function Practical({ navigation }: any): JSX.Element {
  return (
    <View style={styles.container}>
      <Text>Practical Screen</Text>
      <Button title='Go to Home' onPress={() => navigation.navigate(Routes.student.home.STACK)} />
      <Button
        title='Go to Theory'
        onPress={() => navigation.navigate(Routes.student.theory.STACK)}
      />
      <Button
        title='Go to Profile'
        onPress={() =>
          navigation.navigate(Routes.student.home.STACK, { screen: Routes.student.PROFILE })
        }
      />
    </View>
  );
}

const backgroundColor = '#fff';

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    backgroundColor,
    flex: 1,
    justifyContent: 'center',
  },
});

解决方案

核心思路是获取当前激活的Tab对应的Stack名称,然后通过嵌套导航的方式,在该Stack中打开目标页面。

1. 修改CustomDrawerContent,添加获取活跃Stack的逻辑

在自定义抽屉组件中,通过导航状态获取当前激活的Tab对应的Stack名称,然后动态指定导航路径:

function CustomDrawerContent(props: any) {
  // 获取当前激活的Stack名称
  const getActiveStack = () => {
    const drawerState = props.navigation.getState();
    // 找到TabNavigator的路由状态
    const tabRoute = drawerState.routes.find(route => route.name === Routes.MAIN_TAB);
    
    if (tabRoute?.state) {
      // 获取TabNavigator中当前激活的路由(即当前Stack)
      const activeTabIndex = tabRoute.state.index;
      return tabRoute.state.routes[activeTabIndex].name;
    }
    
    // 默认返回Home Stack
    return Routes.student.home.STACK;
  };

  // 通用导航方法
  const navigateToScreen = (screenName: string) => {
    const activeStack = getActiveStack();
    props.navigation.navigate(Routes.MAIN_TAB, {
      screen: activeStack,
      params: {
        screen: screenName
      }
    });
  };

  return (
    <DrawerContentScrollView {...props}>
      <Text>Mi cuenta</Text>
      <DrawerItem
        label='Configuración'
        onPress={() => navigateToScreen(Routes.student.SETTINGS)}
      />
      <DrawerItem
        label='Métodos de pago'
        onPress={() => navigateToScreen(Routes.student.PAYMENT)}
      />
      <Text>Social</Text>
      <DrawerItem
        label='Tiktok'
        onPress={() => navigateToScreen(Routes.student.PROFILE)}
      />
      <Text>Ayuda</Text>
      <DrawerItem
        label='Preguntas frecuentes'
        onPress={() => navigateToScreen(Routes.student.FAQ)}
      />
      <DrawerItem
        label='Atención al alumno'
        onPress={() => navigateToScreen(Routes.student.STUDENT_SUPPORT)}
      />
    </DrawerContentScrollView>
  );
}

2. 关键逻辑说明

  • 获取活跃Stack:通过navigation.getState()获取Drawer的导航状态,从中找到TabNavigator的路由,再通过Tab的index属性拿到当前激活的Stack名称。
  • 嵌套导航:使用navigation.navigate的嵌套参数,先定位到MAIN_TAB,再进入对应的活跃Stack,最后打开目标页面。这种方式会在当前Stack的栈顶添加新页面实例,符合LinkedIn的交互逻辑。

3. 可选优化:封装自定义Hook

如果需要在多个地方使用获取活跃Stack的逻辑,可以封装成自定义Hook:

import { useNavigation } from '@react-navigation/native';
import { Routes } from '@src/constants/routes';

export const useActiveStack = () => {
  const navigation = useNavigation();
  
  const getActiveStack = () => {
    const drawerState = navigation.getState();
    const tabRoute = drawerState.routes.find(route => route.name === Routes.MAIN_TAB);
    
    if (tabRoute?.state) {
      const activeTabIndex = tabRoute.state.index;
      return tabRoute.state.routes[activeTabIndex].name;
    }
    
    return Routes.student.home.STACK;
  };

  return getActiveStack();
};

然后在CustomDrawerContent中使用:

function CustomDrawerContent(props: any) {
  const activeStack = useActiveStack();
  
  const navigateToScreen = (screenName: string) => {
    props.navigation.navigate(Routes.MAIN_TAB, {
      screen: activeStack,
      params: {
        screen: screenName
      }
    });
  };

  // ... 其余代码不变
}

效果验证

修改完成后,在任意Tab下打开抽屉,点击设置、个人资料等选项,页面会在当前Tab对应的Stack中打开,且每个Tab的Stack会独立维护自己的页面栈,实现多实例效果。

内容的提问来源于stack exchange,提问作者Nacho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 02:20:40