I want to separate my navigation code into separate files, 3 files to be precise and I want to load a stack navigator depending on the role of the user, which I will receive on the logging in process (like 3 separate apps), the problem is that if I would've kept the file stacked together it'll be very huge file
here is my main navigation file
import * as React from 'react';
// Apps Navigators
import VendorNavigator from '@vendor-app/routes';
// utils
import Api from '@utils/api';
import AsyncStorage from '@react-native-community/async-storage';
import Container from '@components/container';
// Icons
import Ionicons from 'react-native-vector-icons/Ionicons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
// contexts
const AuthContext = React.createContext(null);
// navigators
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
// import Screens
// Public Screens
import LoginScreen from '@layouts/auth/login';
import PasswordReset from '@layouts/auth/password-reset';
import PasswordResetCode from '@layouts/auth/password-reset-code';
import NewPassword from '@layouts/auth/new-password';
const Navigator = () => {
const AuthStack = createStackNavigator();
const Authenticated = createStackNavigator();
const UnAuthenticated = createStackNavigator();
React.useEffect(() => {
const bootstrapAsync = async () => {
await verifyUser();
let token = await AsyncStorage.getItem('access_token');
if (!token)
dispatch({ type: 'SIGN_OUT' })
else
dispatch({ type: 'SIGN_IN', token: token})
}
bootstrapAsync()
}, [])
const verifyUser = async () => {
let response = await Api.get('/auth/verify-token');
if (response && response.status_code == 200) {
let token = await AsyncStorage.getItem('access_token');
dispatch({ type: 'SIGN_IN', token: token })
} else {
dispatch({ type: 'SIGN_OUT' })
}
}
const authContext = React.useMemo(
() => ({
signIn: async (email_address, password, app) => {
let response = await Api.post(`auth/login`, {
email_address: email_address,
password: password,
});
if (response && response.status_code == 200) {
await AsyncStorage.setItem('access_token', response.data.access_token)
await AsyncStorage.setItem('refresh_token', response.data.refresh_token)
await AsyncStorage.setItem('user', JSON.stringify(response.data.user))
dispatch({ type: 'SIGN_IN', token: response.data.access_token });
}
return response;
},
signOut: async () => {
await AsyncStorage.removeItem('access_token')
await AsyncStorage.removeItem('refresh_token')
await AsyncStorage.removeItem('user')
dispatch({ type: 'SIGN_OUT' })
},
signUp: async data => {
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
},
}),
[]
);
const [state, dispatch] = React.useReducer(
(prevState, action) => {
switch (action.type) {
case 'SIGN_IN':
return {
...prevState,
is_authenticated: true,
token: action.token,
is_loading: false
};
case 'SIGN_OUT':
return {
...prevState,
is_authenticated: false,
token: null,
is_loading: false
}
}
},
{
is_loading: true,
is_authenticated: false,
token: null
}
)
function AuthenticatedStack() {
return (
<Authenticated.Navigator>
{/*
The idea is to have a conditional route here depending on the user's role, for example:
{
user.role == 'vendor'
? <Authenticated.Screen options={{headerShown: false}} name={'Tabs'} component={VendorNavigator} />
: user.role == 'collector'
? <Authenticated.Screen options={{headerShown: false}} name={'Tabs'} component={CollectorNavigator} />
: null
}
*/}
<Authenticated.Screen options={{headerShown: false}} name={'Tabs'} component={VendorNavigator} />
</Authenticated.Navigator>
)
}
function UnAuthenticatedStack() {
return (
<UnAuthenticated.Navigator
screenOptions={{
headerShown: false
}}>
{/* Login and password resets */}
<UnAuthenticated.Screen name={'Login'} component={LoginScreen} />
<UnAuthenticated.Screen name={'PasswordReset'} component={PasswordReset} />
<UnAuthenticated.Screen name={'PasswordResetCode'} component={PasswordResetCode} />
<UnAuthenticated.Screen name={'NewPassword'} component={NewPassword} />
</UnAuthenticated.Navigator>
)
}
return (
<Container blockRender is_loading={state.is_loading}>
<AuthContext.Provider value={authContext}>
<AuthStack.Navigator>
{
state.is_authenticated
? <AuthStack.Screen options={{headerShown: false}} name={'Home'} component={AuthenticatedStack} />
: <AuthStack.Screen options={{headerShown: false}} name={'Login'} component={UnAuthenticatedStack} />
}
</AuthStack.Navigator>
</AuthContext.Provider>
</Container>
)
}
export {AuthContext};
export default Navigator;
and here is my nested stack
import React from 'react';
// utils
import Api from '@utils/api';
import AsyncStorage from '@react-native-community/async-storage';
import Container from '@components/container';
// Icons
import Ionicons from 'react-native-vector-icons/Ionicons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
// navigators
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
// import Screens
import ListScreen from '@layouts/list';
import RetailerScreen from '@layouts/list/retailer';
import RetailerInvoiceScreen from '@layouts/list/retailer/invoice';
import CreateInvoiceScreen from '@layouts/list/retailer/create-invoice';
import CreateUserScreen from '@layouts/list/create-user';
import MapScreen from '@layouts/map';
import SettingsScreen from '@layouts/settings';
function VendorNavigator() {
const tabNavigatorConfig = ({ route }) => ({
tabBarIcon: ({focused, color, size}) => {
let iconName;
if (route.name == 'Retailers')
iconName = focused ? 'store' : 'store-outline';
else if (route.name == 'Map')
iconName = focused ? 'map-sharp' : 'map-outline';
else if (route.name == 'Settings')
iconName = focused ? 'settings' : 'settings-sharp';
return route.name == 'Retailers' ? (
<MaterialCommunityIcons name={iconName} color={color} size={size} />
) : (
<Ionicons name={iconName} color={color} size={size} />
)
}
})
const TabNavigator = createBottomTabNavigator();
const RetailersListStack = createStackNavigator();
function RetailersScreensAndSubScreens() {
return (
<RetailersListStack.Navigator
screenOptions={{
headerShown: false
}}>
<RetailersListStack.Screen name={'Retailers'} component={ListScreen} />
<RetailersListStack.Screen name={'Retailer'} component={RetailerScreen} />
<RetailersListStack.Screen name={'RetailerInvoice'} component={RetailerInvoiceScreen} />
<RetailersListStack.Screen name={'CreateRetailer'} component={CreateInvoiceScreen} />
<RetailersListStack.Screen name={'CreateUser'} component={CreateUserScreen} />
</RetailersListStack.Navigator>
)
}
return (
<TabNavigator.Navigator
screenOptions={tabNavigatorConfig}
tabBarOptions={{
activeTintColor: '#33A788',
inactiveTintColor: '#000000',
}}>
<TabNavigator.Screen name={'Retailers'} component={RetailersScreensAndSubScreens} />
<TabNavigator.Screen name={'Map'} component={MapScreen} />
<TabNavigator.Screen name={'Settings'} component={SettingsScreen} />
</TabNavigator.Navigator>
)
}
export default VendorNavigator;
the app loads initially in a perfect way, but as soon as I navigate, it triggers an infinite re-render.