How to navigate between 2 different navigators after login and logout in react native

Viewed 1534

I am a bit new to react native and I am having difficulty navigate between 2 different tab navigators after login and logout in react native screens of my app that all use function component screens

Can someone advice me on how to do this.

In my Navigation directory(which handle navigation flow), I have 2 files index.js and main.js

  1. navigation/index.js
  2. navigation/main.js

Once the user launches the app, the user sees screens in the navigation/index.js file.

After the user either signs up or signs in, the users see screens in the navigation/main.js file

I have implemented everything else in the app except navigate between navigators after login and logout in the app

I initially put all screens in the same navigator file but I started having issues after pressing the back button continuously

see the code below

:::::::::EDITS::::::::::: No longer using Navigation/main.js Now using only Navigation/index.js and implementing what was suggested by @Tolga Berk Ordanuç Still need more help though because It worked partially Now After logging in by clicking login I get the error

" ERROR The action 'NAVIGATE' with payload {"name":"BottomTabNavigator"} was not handled by any navigator.

Do you have a screen named 'BottomTabNavigator'?"

And when I close the app completely by hitting back numerous times, I am logged in But now second error, when I click logout I get below error

ERROR The action 'NAVIGATE' with payload {"name":"Walkthrough"} was not handled by any navigator.

Do you have a screen named 'Walkthrough'?

home.js

import React, {useState} from 'react';
import {View} from 'react-native';
import {useTheme} from '@config';
import {Header, Icon, Button} from '@components';
import {useDispatch} from 'react-redux';

export default function Home({navigation, route}) {
  const {colors} = useTheme();
     const dispatch = useDispatch();

  const [loading, setLoading] = useState(false);


  const logout = () => {
    setLoading(true);
    dispatch(setIsLoggedIn(false));

    navigation.navigate('SignIn');

    setLoading(false);
  };

  return (
    <View style={{flex: 1}}>
      <Header
        title="sign in"
        renderLeft={() => {
          return (
            <Icon
              name="times"
              size={20}
              color={colors.primary}
              enableRTL={true}
            />
          );
        }}
        onPressLeft={() => {
          navigation.goBack();
        }}
      />
      <View>
        <Button style={{marginTop: 20}} full loading={loading} onPress={logout}>
          Log Out
        </Button>
      </View>
    </View>
  );
}

Navigation/main.js

import React from 'react';
import {createStackNavigator} from '@react-navigation/stack';
import {useTheme} from '@config';
import {useSelector} from 'react-redux';
import {designSelect} from '@selectors';

import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {BaseColor, useFont} from '@config';
import {Icon} from '@components';

/* Main Stack Navigator */
/* Modal Screen only affect iOS */

import Savings from 'Savings';
import Loans from 'Loans';

import Home from 'Home';

import Profile from 'Profile';

/* Stack Screen */
import Setting from 'Setting';
import Feedback from 'Feedback';
import ChangePassword from 'ChangePassword';
import ProfileEdit from 'ProfileEdit';
import ContactUs from 'ContactUs';
import AboutUs from 'AboutUs';
import Support from 'Support';

const MainStack = createStackNavigator();

export default function Main() {
  return (
    <MainStack.Navigator
      screenOptions={{
        headerShown: false,
      }}
      initialRouteName="BottomTabNavigator">
      <MainStack.Screen
        name="BottomTabNavigator"
        component={BottomTabNavigator}
      />
      <MainStack.Screen name="Support" component={Support} />
      <MainStack.Screen name="SavingsTemp" component={Savings} />
      <MainStack.Screen
        name="BottomTabNavigator"
        component={BottomTabNavigator}
      />

      <MainStack.Screen name="Setting" component={Setting} />
      <MainStack.Screen name="Feedback" component={Feedback} />
      <MainStack.Screen name="ChangePassword" component={ChangePassword} />
      <MainStack.Screen name="ProfileEdit" component={ProfileEdit} />
      <MainStack.Screen name="ContactUs" component={ContactUs} />
      <MainStack.Screen name="AboutUs" component={AboutUs} />
    </MainStack.Navigator>
  );
}

function BottomTabNavigator() {
  const {colors} = useTheme();
  const font = useFont();
  const BottomTab = createBottomTabNavigator();
  const design = useSelector(designSelect);

  /**
   * Main follow return  Home Screen design you are selected
   * @param {*} design  ['basic', 'real_estate','event', 'food']
   * @returns
   */
  const exportHome = () => {
    return Home;
  };

  return (
    <BottomTab.Navigator
      initialRouteName="Home"
      screenOptions={{
        headerShown: false,
      }}
      tabBarOptions={{
        showIcon: true,
        showLabel: true,
        activeTintColor: colors.primary,
        inactiveTintColor: BaseColor.grayColor,
        style: {borderTopWidth: 1},
        labelStyle: {
          fontSize: 12,
          fontFamily: font,
          paddingBottom: 4,
        },
      }}>
      <BottomTab.Screen
        name="Home"
        component={exportHome(design)}
        options={{
          title: 'home',
          tabBarIcon: ({color}) => {
            return <Icon color={color} name="home" size={20} solid />;
          },
        }}
      />

      <BottomTab.Screen
        name="Savings"
        component={Savings}
        options={{
          title: 'Savings',
          tabBarIcon: ({color}) => {
            return <Icon color={color} name="bookmark" size={20} solid />;
          },
        }}
      />
      <BottomTab.Screen
        name="Loans"
        component={Loans}
        options={{
          title: 'Loans',
          tabBarIcon: ({color}) => {
            return <Icon color={color} name="clipboard-list" size={20} solid />;
          },
        }}
      />
      <BottomTab.Screen
        name="Profile"
        component={Profile}
        options={{
          title: 'Profile',
          tabBarIcon: ({color}) => {
            return <Icon solid color={color} name="user-circle" size={20} />;
          },
        }}
      />
    </BottomTab.Navigator>
  );
}

Navigation/index.js

   import React, {useEffect, useState} from 'react';
import {StatusBar, Platform} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {DarkModeProvider, useDarkMode} from 'react-native-dark-mode';
import {useTheme, BaseSetting} from '@config';
import i18n from 'i18next';
import {initReactI18next} from 'react-i18next';
import {useSelector} from 'react-redux';
import {languageSelect, designSelect} from '@selectors';

import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {BaseColor, useFont} from '@config';
import {useTranslation} from 'react-i18next';
import {Icon} from '@components';

/* Main Stack Navigator */
/* Modal Screen only affect iOS */
import Loading from 'Loading';

import SignIn from 'SignIn';
import SignUp from 'SignUp';
import ResetPassword from 'ResetPassword';

import Walkthrough from 'Walkthrough';

import Home from 'Home';

import Profile from 'Profile';

import Feedback from 'Feedback';
import ChangePassword from 'ChangePassword';
import ProfileEdit from 'ProfileEdit';
import ContactUs from 'ContactUs';
import AboutUs from 'AboutUs';
import Tab2 from 'Tab2';
import Tab3 from 'Tab3';
import BottomTabNavigator from './BottomTabNavigator';
import {store} from '../store';

const RootStack = createStackNavigator();

export default function Navigator() {
  const language = useSelector(languageSelect);

  const {theme, colors} = useTheme();
  const isDarkMode = useDarkMode();
  const [isLoggedInValue, setIsLoggedInValue] = useState(false);

  useEffect(() => {
    i18n.use(initReactI18next).init({
      resources: BaseSetting.resourcesLanguage,
      lng: language ?? BaseSetting.defaultLanguage,
      fallbackLng: BaseSetting.defaultLanguage,
    });
    if (Platform.OS === 'android') {
      StatusBar.setBackgroundColor(colors.primary, true);
    }
    StatusBar.setBarStyle(isDarkMode ? 'light-content' : 'dark-content', true);
    console.log('<<<<< IS LOGGED IN STATE >>>>>>');
    console.log(isLoggedInValue);
    console.log('<<<<< IS LOGGED IN STATE >>>>>>');
    setIsLoggedInValue(store.getState().isloggedin.isLoggedIn);
  }, [colors.primary, isDarkMode, language, isLoggedInValue]);

  return (
    <DarkModeProvider>
      <NavigationContainer theme={theme}>
        <RootStack.Navigator
          mode="modal"
          screenOptions={{
            headerShown: false,
          }}>
          {!isLoggedInValue ? (
            <>
              <RootStack.Screen
                name="Loading"
                component={Loading}
                options={{gestureEnabled: false}}
              />
              <RootStack.Screen name="Walkthrough" component={Walkthrough} />
              <RootStack.Screen name="SignIn" component={SignIn} />
              <RootStack.Screen name="SignUp" component={SignUp} />
              <RootStack.Screen
                name="ResetPassword"
                component={ResetPassword}
              />
            </>
          ) : (
            <>
              <RootStack.Screen
                name="BottomTabNavigator"
                component={BottomTabNavigator}
              />

              <RootStack.Screen name="Feedback" component={Feedback} />
              <RootStack.Screen
                name="ChangePassword"
                component={ChangePassword}
              />
              <RootStack.Screen name="ProfileEdit" component={ProfileEdit} />
              <RootStack.Screen name="ContactUs" component={ContactUs} />
              <RootStack.Screen name="AboutUs" component={AboutUs} />
            </>
          )}
        </RootStack.Navigator>
      </NavigationContainer>
    </DarkModeProvider>
  );
}

Signin.js

import React, {useState} from 'react';
import {
  View,
  TouchableOpacity,
  KeyboardAvoidingView,
  Platform,
  Alert,
} from 'react-native';
import {useDispatch} from 'react-redux';
import {BaseStyle, useTheme} from '@config';
import {Header, SafeAreaView, Icon, Text, Button, TextInput} from '@components';
import styles from './styles';

export default function SignIn({navigation, route}) {
  const {colors} = useTheme();

  const [loading, setLoading] = useState(false);
  const [id, setId] = useState('');
  const [password, setPassword] = useState('');
  const dispatch = useDispatch();

  /**
   * call when action onLogin
   */
  const onLogin = () => {
    setLoading(true);
 dispatch(setIsLoggedIn(true));
    navigation.navigate('Home');

    setLoading(false);
  };

  const offsetKeyboard = Platform.select({
    ios: 0,
    android: 20,
  });

  return (
    <View style={{flex: 1}}>
      <Header
        title="sign in"
        renderLeft={() => {
          return (
            <Icon
              name="times"
              size={20}
              color={colors.primary}
              enableRTL={true}
            />
          );
        }}
        onPressLeft={() => {
          navigation.goBack();
        }}
      />
      <SafeAreaView style={BaseStyle.safeAreaView} edges={['right', 'left']}>
        <KeyboardAvoidingView
          behavior={Platform.OS === 'android' ? 'height' : 'padding'}
          keyboardVerticalOffset={offsetKeyboard}
          style={{flex: 1}}>
          <View style={styles.contain}>
            <TextInput onChangeText={setId} placeholder="username" value={id} />
            <TextInput
              style={{marginTop: 10}}
              onChangeText={setPassword}
              placeholder="Password"
              secureTextEntry={true}
              value={password}
            />
            <Button
              style={{marginTop: 20}}
              full
              loading={loading}
              onPress={onLogin}>
              'sign_in'
            </Button>
            <TouchableOpacity
              onPress={() => navigation.navigate('ResetPassword')}>
              <Text body1 grayColor style={{marginTop: 25}}>
                'forgot_your_password'
              </Text>
            </TouchableOpacity>
          </View>
        </KeyboardAvoidingView>
      </SafeAreaView>
    </View>
  );
}

5

navigation/BottomTabNavigator.js

   export default function BottomTabNavigator() {

      const {colors} = useTheme();
      const font = useFont();
      const design = useSelector(designSelect);
      const BottomTab = createBottomTabNavigator();
    
      const exportHome = () => {
        return Home;
      };
    
      return (
        <BottomTab.Navigator
          initialRouteName="Home"
          screenOptions={{
            headerShown: false,
          }}
          tabBarOptions={{
            showIcon: true,
            showLabel: true,
            activeTintColor: colors.primary,
            inactiveTintColor: BaseColor.grayColor,
            style: {borderTopWidth: 1},
            labelStyle: {
              fontSize: 12,
              fontFamily: font,
              paddingBottom: 4,
            },
          }}>
          <BottomTab.Screen
            name="Home"
            component={exportHome(design)}
            options={{
              title: 'Home',
              tabBarIcon: ({color}) => {
                return <Icon color={color} name="home" size={20} solid />;
              },
            }}
          />
    
          <BottomTab.Screen
            name="Savings"
            component={Tab2}
            options={{
              title: 'Savings',
              tabBarIcon: ({color}) => {
                return <Icon color={color} name="bookmark" size={20} solid />;
              },
            }}
          />
          <BottomTab.Screen
            name="Loans"
            component={Tab3}
            options={{
              title: 'Loans',
              tabBarIcon: ({color}) => {
                return <Icon color={color} name="clipboard-list" size={20} solid />;
              },
            }}
          />
          <BottomTab.Screen
            name="Profile"
            component={Profile}
            options={{
              title: 'Profile',
              tabBarIcon: ({color}) => {
                return <Icon solid color={color} name="user-circle" size={20} />;
              },
            }}
          />
        </BottomTab.Navigator>
      );
    }
1 Answers

I couldn't see the second tab navigator in your code but i saw that you create navigators inside a component which is in my opinion and experience is not what you supposed to do. you should create your navigators seperately not inside another component. in the documentation of react-navigation they show it this way so they should know what they are doing i guess :D

Login and Main app screens used to managed by switch navigators but with react-navigation 5 they dumped the switch navigators and instead support ternary operators in the navigator components like this

const RootStack = createStackNavigator<RootStackParamList>();
export const RootNavigator = () => {
    const isLoggedIn = useReduxSelector(({ authState }) => authState.isLoggedIn);
    return (
        <RootStack.Navigator screenOptions={{ headerShown: false }}>
            {!isLoggedIn ? (
                <RootStack.Screen
                    name="AuthNavigator"
                    options={{ headerShown: false }}
                    component={AuthNavigator}
                />
            ) : (
                <>
                    <RootStack.Screen name="TabNavigator" component={TabNavigator} />
                    <RootStack.Screen
                        name="DetailsNavigator"
                        component={DetailsNavigator}
                    />
                    <RootStack.Screen
                        name="UserStackNavigator"
                        component={UserStackNavigator}
                    />
                    <RootStack.Screen name="Announcements" component={Announcements} />
                    <RootStack.Screen name="Read" component={Read} />
                    <RootStack.Screen
                        name="Search"
                        options={{
                            ...TransitionPresets.FadeFromBottomAndroid,
                            gestureEnabled: false,
                        }}
                        component={Search}
                    />
                </>
            )}
        </RootStack.Navigator>
    );
};

After logging in and dispatching the appropriate action for your redux to change isLoggedIn state Navigator automatically strips out the Login page and fallbacks to what is under it. and at this point user cannot go to login or register screens even if they go back as many times as they want because we ripped the screens out of the navigation tree.

If you want to return to Login screens you just log out the user and navigation tree again contains these screens and you can navigate to them by navigation.navigate function with respective keys of your screens.

And also if you're curious what my tab navigator looks like it's just like this

const Tab = createBottomTabNavigator<TabParamList>();

//

const tabNavigatorScreenOptions: (props: {
    route: RouteProp<TabParamList, keyof TabParamList>;
    navigation: unknown;
}) => BottomTabNavigationOptions = ({ route }) => ({
    tabBarIcon: (params) => <TabBarIcon {...params} route={route} />,
    tabBarButton: (props) => (
        <Pressable
            {...props}
            style={[props.style, { marginTop: Layout.dimensions.xs_h }]}
        />
    ),
    // tabBarLabel: (props) => <TabBarLabel {...props} route={route} />,
});

export const TabNavigator = () => {
    const debug = useReduxSelector(({ info }) => info.debug);
    // Logger.debug = debug;
    return (
        <Tab.Navigator
            tabBarOptions={{ adaptive: true, showLabel: false }}
            screenOptions={tabNavigatorScreenOptions}
        >
            <Tab.Screen name="Discover" component={Discover} />
            <Tab.Screen name="Library" component={LibraryNavigator} />
            <Tab.Screen name="Settings" component={SettingsNavigator} />
            {debug && <Tab.Screen name="TestNavigator" component={TestNavigator} />}
        </Tab.Navigator>
    );
};

Code is in typescript but that shouldn't be a problem about the logic.

if you have any questions further i am happy to help!

Happy coding my fellow react-native developer!

Related