React-Navigation: Navigation in nested navigators - react-native

I'm developing a react-native app with react-navigation.
Here is my navigation tree:
Root (StackNavigator)
-- Login
-- Main (DrawerNavigator)
---- Screen1
---- Screen2
---- Settings
I have two problems here:
How to reset StackNavigator after user has successfully logged in. So when the user is in Screen1 and presses the back button he doesn't navigate to the login screen again.
How I can navigate from Settings screen to Login screen in order to logout the user?
I'm using React-Navigation v1.2 (and for some particular reason I can't upgrade it to v2).
And here is my code:
const StartNav = StackNavigator({
Login: { screen: Login },
Main: { screen: Main },
});
const Main = DrawerNavigator({
Screen1: {
screen: Screen1
},
Screen2: {
screen: Screen2
},
Setting: {
screen: Setting
}
});
Thanks in advance.

It seems to me you can accomplish everything by simply using a SwitchNavigator? Also from your settings screen you can go back to login by just calling .navigate(Login) and it will find the right one.

Related

React navigation 4, bottom tab navigator: how to open the initial route every time the app is re-opened from the background

Current Behavior
I have a TabNavigator with 4 tabs, each tab is a stackNavigator, and for now each stackNavigator has only one screen: home, maps, reservations, profile. I'm using React Navigation 4.
In every tab I put the navigationOption tabBarOnPress to navigate between the screens, so in this way the backBehavior: 'history' makes sense.
const TabNavigator = createBottomTabNavigator(
{
Home: {
screen: HomeStack,
navigationOptions: {
tabBarOnPress: ({navigation}) => {
navigation.navigate('Home')
},
}
},
//other tabs
},
{
initialRouteName: 'Home',
backBehavior: 'history'
}
)
But if I navigate between screens maybe in the order HOME -> PROFILE -> HOME -> MAPS, the stack of the routers is PROFILE, HOME, MAPS, because of the behavior of the navigate function.
In a similar way, when we are on an android device and push the back button, the order of the visited screens is MAPS -> HOME -> PROFILE and if we push on the back button again we exit from the app, putting it in the background. Now, coming back in the app, the opened screen is PROFILE, and it is also the first route in the navigation stack.
Expected Behavior
When I reopened the app from the background, I don't want to be in the PROFILE screen, but in the HOME screen. I want to reset the first route in navigation stack, changing it with the HOME stack. This would be the same behavior of Instagram (try to go to home -> profile -> home, push the back button two times; now push the back button a third time: the app goes in the background; now reopen Instagram: the app restarts from the home page)
Maybe too late, but add this in your App:
React.useEffect(() => {
const appStateChangeSub = AppState.addEventListener(
"change",
(nextAppState) => {
if (nextAppState === "active") {
NavigationService.navigate("Home");
}
}
);
return () => {
appStateChangeSub.remove();
};
}, []);
NavigationService -> see doc

Push from SingleScreen to TabBased in Wix React Native Navigation

There are documentation and examples of navigating from tab view to other single views. But I can't find any information about navigating from a single view to a tab view.
Is there a possibility to this.props.navigator.push to view with tabs somehow?
If not is there are some workarounds to achieve that?
You should do something like this:
Your main stack navigator:
import HomeScreenTabs from '../yourpath/TabNavigator';
const MainStack = createStackNavigator({
HomeScreen: {
screen: HomeScreenTabs,
},
SingleViewScreen1: {
screen: SomeSingleViewScreen,
navigationOptions: {...}
},
SingleViewScreen2: {
screen: SomeOtherSingleViewScreen,
navigationOptions: {...}
},
}
Your tab navigator:
//Your 3 tab screens
import Home from '../Home';
import Profile from '../Profile ';
import Feedback from '../Feedback ';
const Tabs = createBottomTabNavigator(
{
Home: {
screen: Home
},
Profile: {
screen: Profile
},
Feedback: {
screen: Feedback
}
},
export default Tabs;
And in order to navigate to the Tabs screen from e.g. SingleViewScreen1 you would do this.props.navigation.navigate('HomeScreen').
Use a Wix Navigation v2. It's pretty stable even it says that it is an alpha release. It solves this issue.

Finish current component while navigating to next component using React Native Navigation?

I wanted to close the current component completely while navigating to next component in react-native.
I am using react-navigation for navigating between screens.
Scenario is, I am having two js in my project, Login.js and Home.js. When user logs in into the app it saves the credentials in the AsyncStorage. Every-time when user comes to Login Screen it checks for whether user is logged in already or not. If the user is logged in then app will directly navigate you to the Home page, at this action I want to close the login screen completely.
Currently with my implementation the Login screen remains in to the navigation stack. When I press back from the Home page the app should be closed completely and should not relaunch with login screen again.
Here is my StackNavigator code :
const navigationStack = createStackNavigator(
{
Login: {
screen: LoginScreen
},
Home: {
screen: HomeScreen
},
},
);
For navigating :
this.props.navigation.navigate('Home');
Please let me know what I am doing wrong with my existing code?
You can implement this by multiple ways. Like using replace or reset action on stack Navigator, or using switch Navigator instead of stack Navigator.
Using Reset: (Empty stack and navigate to specified screen)
import { StackActions, NavigationActions } from 'react-navigation';
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Home' })],
});
this.props.navigation.dispatch(resetAction);
Using replace: (replace current screen with the specified screen)
this.props.navigation.replace("Home");
Using Switch Navigator:(Recommended)
const navigationStack = createSwitchNavigator(
{
Login: {
screen: LoginScreen
},
Home: {
screen: HomeScreen
},
},
);
// Navigate to screen
this.props.navigation.navigate("Home");
This can be achieved without having to add back handling code to each and every screen by modifying the getStateForAction method of the particular StackNavigator's router.
const navigationStack = createStackNavigator(
{
Login: {
screen: LoginScreen
},
Home: {
screen: HomeScreen
},
},
);
The getStateForAction method can be modified to achieve this
const defaultStackGetStateForAction =
navigationStack.router.getStateForAction;
navigationStack.router.getStateForAction = (action, state) => {
if(state.index === 0 && action.type === NavigationActions.BACK){
BackHandler.exitApp();
return null;
}
return defaultStackGetStateForAction(action, state);
};
the state.index becomes 0 only when there is one screen in the stack.
You can check with this Back Handling

React-native - How to begin StackNavigator on certain level

I have only one screen and call itself.
module.exports = StackNavigator({
Parent: { screen: PickCategory },
})
Currently, i navigate to screen level i want on componentWillMount.
But i can see the transition, it's ugly and annoying.
I wanna load StackNavigator with specified level without animation.
Is it posible?
Put your screen level want on the first param in StackNavigator.
const TideTable = StackNavigator({
Home: { screen: YourScreenLevelYouWantToDisplayFirst},
Second: { screen: NextScreen},
Third: { screen: TheNextOne},
});

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