Home > Enterprise >  React native (React navigation)- instance of same screen in differents tabs. Navigate from drawer
React native (React navigation)- instance of same screen in differents tabs. Navigate from drawer

Time:11-10

I have an issue with react navigation. I would like to have the same behavior as the linkedIn app has. I mean, in that app, you can open the settings page from the drawer in differents tabs. You can open it from the first tab, second one... and so on. and at the end you get multiple instances.

I am not able to reproduce this behavior

My navigation is: One drawer with one drawer screen (one Tab navigator inside). 3 tabs screens inside that Tab navigator. Each one contains a stack. I have set the same screens in that screen. For example I want to share the Profile, so I have one profile screen in each tab.

In the customDrawer I navigate to screens name, but here is the problem. React navigation does not know what stack should call before calling the right screen. And I cannot find a way to know the current mounted stack so I cannot set it dinamically in order to do inside the custom drawer:

 onPress={() =>
          props.navigation.navigate(Routes.student.home.STACK, { screen: Routes.student.SETTINGS })
        }

Thanks!

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,
  },
});

Theroy.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',
  },
});

CodePudding user response:

Try renaming your Settings screen component differently for each stack navigator. So something like

<PracticalStack.Screen name="Practical/Settings" component={Settings} />

<HomeStack.Screen name="Home/Settings" component={Settings} />

Then you could navigate to the appropriate screen from your drawer based on the tab in focus.

CodePudding user response:

I think I found a possible solution to my question. I have changed the CustomDrawerContent.

function CustomDrawerContent(props: any) {
  const drawerState = props.state.routes[0]?.state;
  const routeIndex = drawerState?.index;
  const focusedTab = drawerState?.routes?.[routeIndex]?.name || Routes.student.home.STACK;

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