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