Authentication Flow in React Native - 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'
}),
]
});

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.

React Navigation: Conditional Main Navigator

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

How to load Home screen without showing login screen after login successful?

There are two stacknavigator for navigate with screens. I want to take condition for isloggedin or not. If loggedin user then select Appstack neither AuthStack. How to do this in this code? Is anyone can suggest me changes?
const AuthStack = createStackNavigator({
Welcome: {screen: WelcomeScreen,
navigationOptions: {
header:null
}},
Login: LoginScreen,
Signup: SignupScreen,
Forgot:ForgotScreen,
});
const AppStack =createStackNavigator(
{
Dashboard: DashboardScreen,
ScanScreen:ScanScreen,
});
export default createSwitchNavigator(
{
App:AppStack,
Auth:AuthStack,
},
{
initialRouteName:'Auth',
}
);
Where you define, and export your routers, define and export a function, which accepts a boolean parameter 'isLoggedIn', and returns the respective router, based on the value of isLoggedIn.
const AuthStack = createStackNavigator({
Welcome: {screen: WelcomeScreen,
navigationOptions: {
header:null
}},
Login: LoginScreen,
Signup: SignupScreen,
Forgot:ForgotScreen,
});
const AppStack =createStackNavigator(
{
Dashboard: DashboardScreen,
ScanScreen:ScanScreen,
});
// This is the function that does the work, it will render
// the either Auth or App router, based on if you're logged in.
export const createRootNavigator = (isLoggedIn = false) => {
if (isLoggedIn === false) {
return <AuthStack/>
} else {
return <AppStack/>
}
};
Then in your app component (or wherever you're rendering your router), we need to import this function, and pass into it whether or not the user is currently logged in.
'file-above.js' is the code above, i.e. the code you gave in your example as well.
Rough example below.
// We import the file
import {createRootNavigator} from './file-above.js'
class App extends Component {
constructor (props) {
super(props);
this.state({
// Switching this will switch your stack router.
loggedIn: false
});
}
render () {
const {loggedIn} = this.state;
// Here we're rendering the router, passing in the
// loggedIn variable from state, this will dictate if the
// Auth or App router is rendered.
return createRootNavigator(loggedIn);
}
}
What is missing here of course is the 'loggedIn' field on the app components state switching, play with the code above, changing the value of loggedIn via the constructor definition, then go hook up to your login form(s), etc.

How to jump to different stack navigator and close previous one with react-navigation?

I am using react-navigation and have 2 distinct stack routers in separate files AuthedRouter and NonAuthedRouter. They each have unrelated screens with no overlap.
AuthedRouter looks like:
const AuthedRouter = StackNavigator({
Home : { screen: Home }
})
NonAuthedRouter.js looks like:
const NonAuthedRouter = StackNavigator({
Signup: { screen : Signup }
SignupTwo : { screen : SignupTwo }
})
In my App.js I check the user app status and in my render method return NonAuthedRouter if user needs to signup. My problem is how do I then navigate to the Home screen in the AuthedRouter once signup is done? Also I want to make sure its dont properly and that the NonAuthedRouter screens are 100% removed from memory/etc.
Thanks.
Instead of having two StackNavigator, u can perform the same using just one.
const MainRouter = StackNavigator({
Signup: { screen : Signup },
SignupTwo : { screen : SignupTwo },
Home : { screen: Home }
})
If the user already signed up, just navigate to Home Screen and the user cannot go back from Home screen because there is no screen in the stack.
If its a new user then navigate to Signup screen, then to SignupTwo screen and ones sign up is completely done just reset the stack and navigate to Home screen. Reset will clear the stack screen. Read react-navigation docs for further information
The way to reset a StackNavigator
this.props.navigation.dispatch(NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Home'})
]
}));
This is how I am Navigating to the "Drawer Stack" after I reset the previous "Login Stack, when I am navigating:"
export const Drawer = createDrawerNavigator(
{
MyAccount: {screen: TabsStack},
},
export const LoginStack = createStackNavigator(
{
Login: {screen: LoginPage},
}
);
export const RootStack = createStackNavigator({
Login: {screen: LoginStack},
Drawer: {screen: Drawer},
},
{
initialRouteName: 'Login',
headerMode: 'none'
},
then on "Login Screen" you do:
on a Button Press/Click:
<TouchableOpacity style={styles.signuptext} onPress={this.onLoginSuccess.bind(this)}>
<Text style={styles.signuptext}> Sign Up Now </Text>
</TouchableOpacity>
you call the "onLoginSuccess" Method like this:
onLoginSuccess = () =>{
{
const navigateAction = StackActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: 'Drawer' })]
})
this.props.navigation.dispatch(navigateAction)
}
Note: This basically resets the "Login" stack while navigating you to the "Drawer" Stack. So now when you will press the back button, you cannot return to the Login Screen of the "Login Stack".

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