React Navigation: Conditional Main Navigator - react-native

I am developing a mobile application in which when the user opens the application if the user is signed in then he lands on the homepage and if the user is not signed in then he lands on the sign Page and the user is signed in or not we store its information in the local storage
So my question is how to how to conditionally switch the application root navigator on the basis of value which in the local storage
Following is the code i am using in the navigator
const BottomTabNavigator = createMaterialBottomTabNavigator({
Home: { screen: HomePage },
Library: { screen: LibrarePage },
},
{
initialRouteName: 'Home',
activeColor: '#f0edf6',
inactiveColor: '#3e2465',
barStyle: { backgroundColor: '#694fad' },
});
const loginNavigator = createSwitchNavigator({ SignInPage, BottomTabNavigator });
const AppContainer = createAppContainer((AsyncStorage.getItem("IsUserLoggedIn")) ? BottomTabNavigator : loginNavigator );
export default AppContainer;
Above code is not working as the AsyncStorage.getItem("IsUserLoggedIn") is the async method so my question is how i can achieve the above scenario

Related

How to show TabNavigator only for some screens but not for others? - The component for route 'TabNavigator' must be a React component

User should go first to the Authentication screen and, once authenticated, he should be redirected to the Tab Navigation screens.
I tried the following approach, which didn't work. The error I got was - The component for route 'TabNavigator' must be a React component:
const AppNavigator = createStackNavigator(
{
Authentication: Authentication,
TabNavigator: TabNavigator
},
{
initialRouteName: "Authentication"
}
);
const TabNavigator = createBottomTabNavigator(
{
Profile: { screen: Profile },
Dashboard: { screen: Dashboard },
History: { screen: History }
},
{
initialRouteName: "Dashboard"
}
);
export default createAppContainer(AppNavigator);
How can I make this happen?
Thanks for your help.

Authentication Flow in React Native

I'm having issue creating the authentication flow of my app. What I actually want is to navigate user conditionally based on their role.
By default, I have created an AuthStack which is basically a stackNavigator and it has a login page. Once user logs in, we receive user's role through a network request. Next I navigate him to a simple home page that returns nothing but switchNavigator based on his role. Here's the code for better clarity.
const AuthStack = createStackNavigator({
Login: {
screen: Login
},
SignUp: {
screen: SignUp
},
Home: {
screen: Home
}
},
{
initialRouteName: 'Login',
headerMode: 'none'
});
const AppContainer = createAppContainer(AuthStack);
const Navigation = () => {
return <AppContainer />
}
When user logs in, I redirect him to Home screen shown in above mentioned stack. Here's the code in Home screen:
const Home = (props) => {
const AppContainer = createAppContainer(RootNavigator(props.user.role))
return <AppContainer />
}
Here I create a new app container (which might be the bad practice, advice please). And RootNavigator is a helper function which returns a switchNavigator:
export const RootNavigator = (user) => {
return createSwitchNavigator({
Admin: {
screen: AdminDrawerNavigator
},
Reporter: {
screen: ReporterDrawerNavigator
}
},
{
initialRouteName: user === 'admin'? 'Admin': 'Reporter'
})
}
This all works fine but it seems switchNavigator doesn't seem to work correctly. If I press back button of hardware, it goes back to login page. Thank you for your time. Please suggest the possible solution.
Try to reset you history navigation :
NavigationActions.reset({
index: 1,
actions: [NavigationActions.navigate({
routeName: 'Admin'
}),
]
});

How to remove screens (unmount component) from react native drawer-navigator on log out?How to reload Components Data?

I am using react navigation v3 in my app, I use stack navigator inside drawer navigator ,On the click of logout I navigate to login screen with clearing storage of user, But whenever I login again , Main component dose not call componentWillMount or componentDidMount method , and displays Previously loaded data on it. here is my code >
const screens = {
login: { screen: Login },
dashboard: { screen: Dashboard },
patientList:{screen:StackNav},
headerComponent:HeaderComponent
}
const MyDrawerNavigator = createDrawerNavigator(
screens,
{
initialRouteName: 'login',
contentComponent: Sidebar
}
);
App = createAppContainer(MyDrawerNavigator);
export default App;
StackNav ==
export default createStackNavigator({
PatientList,
PatientDetails
});
Logout Function ==
localStorageService.removeAllKeys().then((res) => {
this.props.navigation.navigate(route, { isLogin: 'N' })
});
In React Navigation 5.x
use unmountOnBlur
<Drawer.Screen
name={...}
component={...}
unmountOnBlur={true}
options={{unmountOnBlur: true}}
/>
put this in navigationOptions of drawer navigator
unmountInactiveRoutes: true
As Daniyal's awnser complementation: I've introduced the configuration inside createDrawerNavigator() configs as above:
const AppNavigator = createDrawerNavigator(
{
Home:{
screen: Home
},
Login:{
screen: Login,
navigationOptions: {
drawerLabel: () => null
}
}
},
{
unmountInactiveRoutes: true
});
And now all pages are working withouth any "cache".

Route to Login screen in react native in case a user is not authenticated

I've seen a lot of examples how it is done with other libraries but I'm using react-navigation one and need a solution for it.
Here is a code example:
const MainTabs = TabNavigator({
Profile: { screen: ProfileScreen },
Home: { screen: HomeScreen },
});
const RootNavigator = StackNavigator({
Main: { screen: MainTabs },
Login: { screen: LoginScreen },
},{
initialRouteName: "Login",
headerMode: "none",
});
export default RootNavigator;
Full app code you can find here: https://snack.expo.io/Hy8IXWppg
User authentication status is checked on my server.
I want Login screen to be rendered for a user on the app launch in case the user is not authenticated but ideally, it would be nice to route the user to Login in case his session is deleted/expired on the server.
In other words, I want to be able to do that at any time from any code part.
I know I can do this with code like below
if(!userIsLoggedIn()) {
this.props.navigation.dispatch(
NavigationActions.navigate({routeName: 'Login'}) );
}
inside a screen component (where userIsLoggedIn is my custom auth checking method), but I don't want to perform this action manually in each component. But even I wouldn't have another choice, where to put userIsLoggedIn() check? Inside consctructor/componentWillMount/componentWillUpdate?
UPDATE
I expected a solution which would allow me to specify all screens/routes which require authentication.
It seems to me that I have to use routes here with some custom rules.
But there are two kinds of standard routers: TabRouter and StackRouter.
I'm going to have 3-level navigation
StackNavigator
LoginScreen
RegisterScreen
AboutScreen
FirstLevelTabNavigator
Tab1: AccountScreen
Tab2: SecondLevelTabNavigator
Tab1Screen
Tab2Screen
Tab3Screen
Obviously, Login, Register and About screens don't require authentication, but on an attempt to access Account, Tab1Screen, Tab2Screen or Tab3Screen users must be redirected to LoginScreen.
Should I construct 3 routers for each navigator now?
I think you should have another screen which would be your first screen. This can be a simple component which would just be an ActivityIndicator. In that you should check with your server and navigate accordingly.
Ex.
InitialComponent
componentWillMount() {
getStatus(this.navigate.bind(this))
}
navigate(loggedIn) {
this.setState({animating: false});
if(loggedIn) {
navigate to Main
} else {
navigate to Login
}
}
render() {
return(<View> <ActivityIndicator animating={this.state.animating}/> </View>);
}
This would also have benefit of showing your user that you are processing something, before redirecting them(For example if the request takes time).
You need to use SwitchNavigator for this. The React Navigation docs explain how to do an authentication flow here:
https://reactnavigation.org/docs/en/auth-flow.html
The example below is from their docs:
import { createSwitchNavigator, createStackNavigator } from 'react-navigation';
// Implementation of HomeScreen, OtherScreen, SignInScreen, AuthLoadingScreen
// goes here.
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
);
Then you navigate doing something like this:
this.props.navigation.navigate(userToken ? 'App' : 'Auth');

React native login state across entire app

My home screen exists of a StackNavigator which is inside a DrawerNavigator. Code:
const HomeNavigator = StackNavigator({
Index: { screen: ScreenHome },
Register: { screen: ScreenRegister },
});
const App = DrawerNavigator({
Home: { screen: HomeNavigator }
},{
contentComponent: (navigation) => <ScreenMenu navigation={navigation}/>
});
AppRegistry.registerComponent('ReactApp', () => App);
After logging in through a REST Service i want to pass the loggedIn state to all my other screens. But how should i do this? Should i declare a Authentication Component which will be passed to all the screens? Or is there another best practice for a global login state?
You could use redux for setting up a global state for the app.