Expo Tab Navigation and Stack Navigation

1.4k views Asked by At

Hi I am trying to add Tab Navigation when user is logged in and Stack Navigation when User is not logged in. So I have wrote a logic but I don't know why I am getting errors. Kindly help me out.

I wanted to show Tab Navigation only when user has successfully logged in I am using Nodejs backend and Mongodb as database.

This is my AppNavigator file.

AppNavigator.js

import React, { useEffect, useState } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { NavigationContainer } from '@react-navigation/native';
import SignUp from "../screens/SignupScreen";
import LoginScreen from "../screens/LoginScreen";
import LoadingScreen from "../screens/LoadingScreen";
import HomeScreen from "../screens/HomeScreen";
import AsyncStorage from '@react-native-async-storage/async-storage';
const Stack = createStackNavigator();

const Tab = createBottomTabNavigator()
const [isLoggedin, setLogged] = useState(null)
const TabNavigator = () => {
  return <TabNavigator>
    <Tab.Screen name='Home' component={HomeScreen} />
    <Tab.Screen name='Profile' component={AccountScreen} />
    <Tab.Screen name='Home' component={HomeScreen} />
  </TabNavigator>
}
const AuthNavigator = () => {
  return (
  <Stack.Navigator
    headerMode="none"
    screenOptions={{
      headerStyle: { elevation: 0 },
      cardStyle: { backgroundColor: '#fff' }
    }}
  >


    <Stack.Screen name="loading" component={LoadingScreen}></Stack.Screen>
    <Stack.Screen name="home" component={HomeScreen}></Stack.Screen>

    <Stack.Screen name="login" component={LoginScreen}></Stack.Screen>
    <Stack.Screen name="signup" component={SignUp}></Stack.Screen>


  </Stack.Navigator>
  )
};
const Navigation = () => {
  const [user, setUser] = useState('');
  useEffect(() => {
      if (isLoggedin) {
        setUser(userExist)
      }
      else {
        setUser('')
      }
    
  }, [])
  return (
    <NavigationContainer >
      {user ? <TabNavigator /> : <AuthNavigator />}
    </NavigationContainer>
  )
};
function AppNavigator() {

  useEffect(() => {
    const token = AsyncStorage.getItem('token')
    if (token) {
      setLogged(true)
    }
    else {
      setLogged(false)
    }
  }, [])
  return (
    <Navigation />
  );
}

export default AppNavigator;

This is my App.js file

App.js

import React,{useEffect,useState} from "react";
import { NavigationContainer } from "@react-navigation/native";
import AppNavigator from "./navigation/AppNavigator";

export default function App() {
  return (
      <AppNavigator />

  );
}

I have my own LoadingScreen file

LoadingScreen.js

import { StatusBar } from 'expo-status-bar';
import React,{useEffect,useState} from 'react';
import { StyleSheet, ActivityIndicator, Image,View,Text } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Button, TextInput } from 'react-native-paper';

// <View style={styles.container}>
export default function LoadingScreen(props) {
    
    const detectLogin = () =>{
        const token = AsyncStorage.getItem('token')
        if (token) {
            props.navigation.navigate("profile")
        }
        else {
            props.navigation.navigate("login")
        }
    }
    useEffect(() => {
       detectLogin()
      }, [])
    return (
        <View style={styles.container}>
        <ActivityIndicator size="large" color="black">
         
        </ActivityIndicator>
        </View>
    );
}

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

2

There are 2 answers

8
Kartikey On BEST ANSWER

I've created a Snack for you to see the working example

The first thing that I Noticed is that in your TabNavigator function you wrote

return <TabNavigator>
    <Tab.Screen name='Home' component={HomeScreen} />
    <Tab.Screen name='Profile' component={AccountScreen} />
    <Tab.Screen name='Home' component={HomeScreen} />
  </TabNavigator>

Instead you have to write something like this

return <Tab.Navigator> // Notice the . here
    <Tab.Screen name='Home' component={HomeScreen} />
    <Tab.Screen name='Profile' component={AccountScreen} />
    <Tab.Screen name='Home' component={HomeScreen} />
  </Tab.Navigator>

Your token restoration logic is not efficient I guess.

I would suggest you to make two different Navigators and perform token checking logic inside your App.js

What you should do is, create a folder called navigation where your App.js is located.

Then inside navigation create two files AppNavigator.js and AuthNavigator.js

Your AppNavigator.js should look like this -

import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

import HomeScreen from '../screens/HomeScreen';
import AccountScreen from '../screens/AccountScreen';

const Tab = createBottomTabNavigator();

function AppNavigator() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Accounts" component={AccountScreen} />
    </Tab.Navigator>
  );
}

export default AppNavigator;

Your AuthNavigator.js should look like this -

import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';

import LoadingScreen from '../screens/LoadingScreen';
import HomeScreen from '../screens/HomeScreen';
import LoginScreen from '../screens/LoginScreen';
import SignUp from '../screens/SignUpScreen';

const Stack = createStackNavigator();

function AuthNavigator() {
  return (
    <Stack.Navigator
      headerMode="none"
      screenOptions={{
        headerStyle: { elevation: 0 },
        cardStyle: { backgroundColor: '#fff' },
      }}>
      <Stack.Screen name="loading" component={LoadingScreen}></Stack.Screen>
      <Stack.Screen name="home" component={HomeScreen}></Stack.Screen>

      <Stack.Screen name="login" component={LoginScreen}></Stack.Screen>
      <Stack.Screen name="signup" component={SignUp}></Stack.Screen>
    </Stack.Navigator>
  );
}

export default AuthNavigator;

Then you should install expo-app-loading from here

Then in your App.js

import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import AppLoading from 'expo-app-loading';
import AsyncStorage from '@react-native-async-storage/async-storage';

import AppNavigator from './navigation/AppNavigator';
import AuthNavigator from './navigation/AuthNavigator';

export default function App() {
  const [user, setUser] = useState('');
  const [IsReady, setIsReady] = useState(false);

  const RestoreUser = async () => {
    const token = await AsyncStorage.getItem('token');
    if (token) {
      setUser(true);
    } else {
      setUser(false);
    }
  };

  if (!IsReady) {
    return (
      <AppLoading
        startAsync={RestoreUser}
        onFinish={() => setIsReady(true)}
        onError={() => {}}
      />
    );
  }

  return (
    <NavigationContainer>
      {user ? <AppNavigator /> : <AuthNavigator />}
    </NavigationContainer>
  );
}

This is the most efficient way of authentication that I use in all my projects..

0
Abdullah Mujahid On

Apparently I have to remove these two from my code in order to work correctly, it's resolved now. Thankyou.

import { StatusBar } from 'expo-status-bar';

<StatusBar style="dark" backgroundColor="black" barStyle="light-content" />