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

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

Related

React Navigation - goBack() is automatically invoked for stack navigator screen

I have 3 stack navigator screens (Home, Item List, Item Detail -> same order) inside drawer navigator and all three screens are hooked up to redux store with connect() helper.
When I do navigate('ItemDetail') from ItemList, it navigates to the screen and immediately comes back to ItemList screen. I am not sure why.
Following is my navigation structure -
const AppStack = createStackNavigator(
{
Home: {
screen: HomeScreen
},
ItemList: {
screen: ItemListScreen
},
ItemDetail: {
screen: ItemDetailScreen
}
},
{
initialRouteName: 'Home'
}
);
const DrawerNav = createDrawerNavigator(
{
DrawerApp: AppStack
},
{
drawerPosition: 'right'
}
);
const SwitchStack = createSwitchNavigator(
{
Loading: Loading,
Auth: AuthStack,
App: DrawerNav
},
{
initialRouteName: 'Loading'
}
);
This is how my each navigation screen component looks -
export class ProviderListScreen extends Component {
render() {
const { navigation } = this.props;
// ItemList is hooked up to Redux via connect() helper
return <ItemList navigation={navigation} />;
}
On my ItemDetail component, I get the Item data through route params from ItemList screen and I also dispatch an action (To reset some part of the store state) in component did mount. As soon as I do that, previous screen (ItemList) is automatically rendered.
Inside item detail, I make API call to create booking for that item and the booking object is managed by redux. Once I land on the ItemDetail, I reset the booking object for new booking data.
Here is the snippet of ItemDetail's componentDidMount -
componentDidMount() {
this.props.resetItembooking();
}
I am not sure what is causing this behaviour. If I remove the ItemList screen and jump directly to ItemDetail screen from HomeScreen, this issue does not occur.
Any help is appreciated.
I had the exact same problem however I tried the answer given in the original post comments sections given by Awadhoot but this did not work for me.
For anyone still trying to solve this issue, ensure you do not have any recurring intervals setup. Therefore you should always clear intervals before navigating away:
clearInterval(this.intervalId);
In react-navigation 5 you can use the useIsFocused hook for that:
import { useIsFocused } from '#react-navigation/native';
// ...
function Profile() {
const isFocused = useIsFocused();
return <Text>{isFocused ? 'focused' : 'unfocused'}</Text>;
}
From the docs: https://reactnavigation.org/docs/use-is-focused/

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".

Two routes instantiate when "Registration" is triggered

I have an app with a registration and a login flow. And I'm having bit of an issue with nested stack navigators when used with Redux.
React-Navigation structure
RootNavigator (Stack navigator)
Registration (Stack navigator)
Login (Stack navigator)
App setup
react: 16.0.0-alpha.6,
react-native: 0.44.2,
react-navigation: ^1.0.0-beta.11,
react-redux: ^5.0.6,
My issue is that when 'Registration' (or 'Login') is triggered from 'Landing' ReactNavigation instantiate two initial screens for the nested (internal) stack navigator. Please see the screen grabs bellow.
This is the landing state (which is correct)
But when 'Register' is pressed, this is what happens.
I end up with two routes for 'Registration' (routeName is called 'PersonalRego'). As shown in the screen shot, 'Step1' is duplicated where as I was expecting to only have one 'Step1' instance in the routes array. But the keys for those two 'Step1' are different. One starts with 'Init-' the other one doesn't.
As a result, navigating back (pressing 'Close' button on top) does not dismiss registration as per expectation. It rather shows the 'Step1' of registration again.
Other than this issue, everything with react-navigation seems to be workinf fine for me. Is there anything special that I need to do when configuring nested StackNavigators in a ReactNative + ReactNavigation + Redux app?
I'm a bit baffled by this. Any comments are gladly welcomed.
P.s : StackNavigator code (just one level deep nested StackNavigator)
// LandingNav.js
const routes = {
Landing: { screen: Landing },
PersonalRego: { screen: PersonalRego },
Auth: { screen: Auth },
};
const config = {
headerMode: 'none',
initialRoute: 'Landing,
};
const Root = StackNavigator(routes, config);
.
// PersonalRegoNav.js
const routes = {
Step1: { screen: Step1 },
Step2: { screen: Step2 },
Step3: { screen: Step3 },
};
const config = {
headerMode: 'none,
initialRoute: 'Step1',
};
const Root = StackNavigator(routes, config);
.
// AuthNav.js
const routes = {
Login: { screen: LoginContainer },
ForgotUsername: { screen: ForgotUsername },
ForgotPassword: { screen: ForgotPassword },
};
const config = {
headerMode: 'none',
initialRoute: 'Login',
};
const Root = StackNavigator(routes, config);
Answering my own question for posterity's sake.
When Register is pressed, I'm navigating to 'Step1' instead of 'PersonalRego'. 'PersonalRego' already has 'Step1' as an initial route, so if you navigate to 'Step1', it pushes another instance.
So calling 'PersonalRego' route on Register button press fixes the issue.
Please refer to the link below, beautifully solved & explained by my friend -> https://medium.com/#kakul.gupta009/custom-drawer-using-react-navigation-80abbab489f7

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-Navigation: Navigation in nested navigators

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.