Home > Software engineering >  React native login and loggedin navigation - correct way advise
React native login and loggedin navigation - correct way advise

Time:12-13

Please can someone advise the right way to have a system for login and logged in navigations. The way i am doing right now is i use a shared state using use-between package for shared state and show correct navigation. But i am not sure this is the right way to do. Please can someone point in the right direction and the right way to achieve the same. Below is my code.

import React, { useEffect } from "react"; 
import { useLoggedInUserState } from "./core/state"; 
import { useBetween } from "use-between";
import { getLoggedInUser } from "./core/utils";

//navigation
import LoginNav from "./nav/LoginNav";
import LoggedInNav from "./nav/LoggedInNav";

export default function App() {

  //shared states
  const { loggedInUser, setLoggedInUser } = useBetween(useLoggedInUserState);

  //check storage and store user in state
  useEffect(async () => {
    const objUser = await getLoggedInUser();
    setLoggedInUser(objUser);
  }, []);

  //render
  return loggedInUser ? <LoggedInNav loggedInUser={loggedInUser} /> : <LoginNav />;
}

//login nav

Logged in nav

import React, { memo } from "react";

import { createDrawerNavigator } from "@react-navigation/drawer";
import { NavigationContainer, CommonActions, useNavigation } from "@react-navigation/native";
import { Appbar, BottomNavigation, Menu, useTheme, Button } from "react-native-paper";
import { Text, Image, View, TouchableOpacity } from "react-native";

import { AboutScreen, TermsScreen, CartScreen, PaymentScreen, NotificationTestScreen, VehicleEditScreen } from "../screens";

import Sidebar from "../components/Sidebar";
import BottomNav from "./BottomNav";

const Drawer = createDrawerNavigator();

//LoggedIn Navigation
const LoggedInNav = (propsLoggedIn) => {
  const { colors, background } = useTheme();

  //const [visible, setVisible] = React.useState(false); //used by appbar
  //const openMenu = () => setVisible(true);  //used by appbar
  //const closeMenu = () => setVisible(false); //used by appbar
  const cartCount = 40; //will be from state

  return (
    <NavigationContainer>
      <Drawer.Navigator
        screenOptions={(propsNavigator) => ({
          headerShown: true,
          headerLeft: () => (
            <View
              style={{
                flex: 1,
                alignItems: "center",
                justifyContent: "center",
                flexDirection: "row",
              }}
            >
              <TouchableOpacity onPress={() => propsNavigator.navigation.openDrawer()}>
                <Image
                  source={require("../assets/images/menu.png")}
                  style={{
                    tintColor: "#fff",
                    width: 28,
                    height: 28,
                    position: "relative",
                    marginLeft: 8,
                  }}
                />
              </TouchableOpacity>

              <TouchableOpacity onPress={() => propsNavigator.navigation.navigate("Dashboard")}>
                <Image
                  source={require("../assets/images/home.png")}
                  style={{
                    tintColor: "#fff",
                    width: 28,
                    height: 28,
                    position: "relative",
                    marginLeft: 8,
                  }}
                />
              </TouchableOpacity>
            </View>
          ),

          headerRight: () => (
            <View
              style={{
                flex: 1,
                alignItems: "center",
                justifyContent: "center",
                flexDirection: "row",
              }}
            >
              <TouchableOpacity
                onPress={() => {
                  propsNavigator.navigation.navigate("NotificationTest");
                }}
              >
                <Image
                  source={require("../assets/images/bell.png")}
                  style={{
                    tintColor: "#fff",
                    width: 32,
                    height: 32,
                    position: "relative",
                    marginRight: 4,
                  }}
                />
              </TouchableOpacity>

              <TouchableOpacity
                onPress={() => {
                  propsNavigator.navigation.navigate("Cart");
                }}
              >
                <Image
                  source={require("../assets/images/cart.png")}
                  style={{
                    tintColor: "#fff",
                    width: 32,
                    height: 32,
                    position: "relative",
                    marginRight: 16,
                  }}
                />
              </TouchableOpacity>
              {cartCount > 0 ? (
                <View
                  style={{
                    position: "absolute",
                    backgroundColor: "red",
                    width: 16,
                    height: 16,
                    borderRadius: 15 / 2,
                    right: 10,
                    top:  8,
                    alignItems: "center",
                    justifyContent: "center",
                  }}
                >
                  <Text
                    style={{
                      alignItems: "center",
                      justifyContent: "center",
                      color: "#FFFFFF",
                      fontSize: 8,
                    }}
                  >
                    {cartCount}
                  </Text>
                </View>
              ) : null}
            </View> 
          ),
          headerStyle: {
            backgroundColor: colors.primary,
          },
          headerTintColor: "#fff",
          headerTitleStyle: {
            fontWeight: "bold",
          },
          /* header using react native paper 
          header: ({ navigation, back, route }) => (
            <Appbar.Header>
              {back ? <Appbar.BackAction onPress={navigation.goBack} /> : null}
              <Appbar.Content title={route.params.title ? route.params.title : "Between Detailing"} />
              {!back ? (
                <Menu visible={visible} onDismiss={closeMenu} anchor={<Appbar.Action icon="menu" color="white" onPress={openMenu} />}>
                  <Menu.Item
                    onPress={() => {
                      navigation.navigate("Dashboard");
                    }}
                    title="Dashboard"
                  />
                  <Menu.Item
                    onPress={() => {
                      navigation.navigate("About");
                    }}
                    title="About"
                  />
                  <Menu.Item
                    onPress={() => {
                      navigation.navigate("Terms");
                    }}
                    title="Terms"
                    //disabled
                  />
                </Menu>
              ) : null}
            </Appbar.Header>
          ),header using react native paper  */
        })}
        drawerContent={(drawerContentProps) => <Sidebar loggedInUser={propsLoggedIn.loggedInUser} {...drawerContentProps} />}
      >
        <Drawer.Screen name="Dashboard" component={BottomNav} initialParams={{ title: "Dashboard" }} />
        <Drawer.Screen name="About" component={AboutScreen} initialParams={{ title: "About" }} />
        <Drawer.Screen name="Terms" component={TermsScreen} initialParams={{ title: "Terms" }} />
        <Drawer.Screen name="VehicleEdit" component={VehicleEditScreen} initialParams={{ title: "Edit Vehicle" }} />
        <Drawer.Screen name="Cart" component={CartScreen} initialParams={{ title: "Cart" }} options={{ drawerItemStyle: { display: "none" } }} />
        <Drawer.Screen name="Payment" component={PaymentScreen} initialParams={{ title: "Payment" }} options={{ drawerItemStyle: { display: "none" } }} />
        <Drawer.Screen name="NotificationTest" component={NotificationTestScreen} initialParams={{ title: "NotificationTest" }} options={{ drawerItemStyle: { display: "none" } }} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
};

export default memo(LoggedInNav);

CodePudding user response:

React native is unopinionated on how you can structure your authenticated-based navigation.

As you're using React Navigation, the team provided guidance here: https://reactnavigation.org/docs/auth-flow

CodePudding user response:

Thanks for pointing to that link, i think i am pretty much in the right direction.. i have modified my code a bit as per the article and it is working perfect.

MY ONLY CONCERN IS THAT I HAVE 2 NAVIGATION CONTAINERS 1 IN BOTH LoginNav and LoggedInNav as below. WHAT IS THE OPINION IN THIS CASE, IS IT RIGHT TO DO IT LIKE THIS?

//login nav

const LoginNav = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator
        screenOptions={{
          headerShown: false,
        }}
        initialRouteName="Home"
      >
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Login" component={LoginScreen} />
        <Stack.Screen name="Register" component={RegisterScreen} />
        <Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
        <Stack.Screen name="Dashboard" component={DashboardScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};  

//logged in nav

//LoggedIn Navigation
const LoggedInNav = (propsLoggedIn) => {
  const { colors, background } = useTheme();

  //const [visible, setVisible] = React.useState(false); //used by appbar
  //const openMenu = () => setVisible(true);  //used by appbar
  //const closeMenu = () => setVisible(false); //used by appbar
  const cartCount = 40; //will be from state

  return (
    <NavigationContainer>
      <Drawer.Navigator
         
        drawerContent={(drawerContentProps) => <Sidebar loggedInUser={propsLoggedIn.loggedInUser} {...drawerContentProps} />}
      >
        <Drawer.Screen name="Dashboard" component={BottomNav} initialParams={{ title: "Dashboard" }} />
        <Drawer.Screen name="About" component={AboutScreen} initialParams={{ title: "About" }} />
        <Drawer.Screen name="Terms" component={TermsScreen} initialParams={{ title: "Terms" }} />
        <Drawer.Screen name="VehicleEdit" component={VehicleEditScreen} initialParams={{ title: "Edit Vehicle" }} />
        <Drawer.Screen name="Cart" component={CartScreen} initialParams={{ title: "Cart" }} options={{ drawerItemStyle: { display: "none" } }} />
        <Drawer.Screen name="Payment" component={PaymentScreen} initialParams={{ title: "Payment" }} options={{ drawerItemStyle: { display: "none" } }} />
        <Drawer.Screen name="NotificationTest" component={NotificationTestScreen} initialParams={{ title: "NotificationTest" }} options={{ drawerItemStyle: { display: "none" } }} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
};

//navigation

import LoginNav from "./nav/LoginNav";
import LoggedInNav from "./nav/LoggedInNav";

export default function App() {
  //shared states
  const { loggedInUser, setLoggedInUser } = useBetween(useLoggedInUserState);

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

  //check storage and store user in state
  useEffect(async () => {
    setLoading(true);
    const objUser = await getLoggedInUser();
    setLoggedInUser(objUser);
    setLoading(false);
  }, []);

  //render
  if (!loading) {
    return loggedInUser ? <LoggedInNav loggedInUser={loggedInUser} /> : <LoginNav />;
  } else {
    return (
      <View>
        <Text>Loading...</Text>
      </View>
    );
  }
}

CodePudding user response:

Recommended best practice is having a single NavigationContainer.I think that having a single source of truth for all your navigation state is important.

Try to refactor code as below:

export default function App() {
  //shared states
  const { loggedInUser, setLoggedInUser } = useBetween(useLoggedInUserState);

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

  //check storage and store user in state
  useEffect(async () => {
    setLoading(true);
    const objUser = await getLoggedInUser();
    setLoggedInUser(objUser);
    setLoading(false);
  }, []);

  //render
  return !loading ? (
    <NavigationContainer>
      {loggedInUser ? (
        <Drawer.Navigator
          drawerContent={(drawerContentProps) => (
            <Sidebar
              loggedInUser={propsLoggedIn.loggedInUser}
              {...drawerContentProps}
            />
          )}
        >
          <Drawer.Screen
            name="Dashboard"
            component={BottomNav}
            initialParams={{ title: "Dashboard" }}
          />
          <Drawer.Screen
            name="About"
            component={AboutScreen}
            initialParams={{ title: "About" }}
          />
          <Drawer.Screen
            name="Terms"
            component={TermsScreen}
            initialParams={{ title: "Terms" }}
          />
          <Drawer.Screen
            name="VehicleEdit"
            component={VehicleEditScreen}
            initialParams={{ title: "Edit Vehicle" }}
          />
          <Drawer.Screen
            name="Cart"
            component={CartScreen}
            initialParams={{ title: "Cart" }}
            options={{ drawerItemStyle: { display: "none" } }}
          />
          <Drawer.Screen
            name="Payment"
            component={PaymentScreen}
            initialParams={{ title: "Payment" }}
            options={{ drawerItemStyle: { display: "none" } }}
          />
          <Drawer.Screen
            name="NotificationTest"
            component={NotificationTestScreen}
            initialParams={{ title: "NotificationTest" }}
            options={{ drawerItemStyle: { display: "none" } }}
          />
        </Drawer.Navigator>
      ) : (
        <Stack.Navigator
          screenOptions={{
            headerShown: false,
          }}
          initialRouteName="Home"
        >
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Login" component={LoginScreen} />
          <Stack.Screen name="Register" component={RegisterScreen} />
          <Stack.Screen
            name="ForgotPassword"
            component={ForgotPasswordScreen}
          />
          <Stack.Screen name="Dashboard" component={DashboardScreen} />
        </Stack.Navigator>
      )}
    </NavigationContainer>
  ) : (
    <View>
      <Text>Loading...</Text>
    </View>
  );
}


  • Related