I am using React Navigation 5 in a project, and I'm having trouble trying to block a user from navigating back after a certain point.
The app uses a nested navigation structure similar to this:
ROOT (STACK)
|-- LoginScreens (STACK - options={{ gestureEnabled: false }} )
| |-- Login (SCREEN) -> when successful navigate to "Home"
| +-- Register (SCREEN) -> after registration, navigate to "Login"
|
+-- Home (TABS - options={{ gestureEnabled: false }} )
|-- BlahBlah (SCREEN)
|-- MyProfile (SCREEN)
+-- Dashboard (TABS)
|-- AllTasks (SCREEN)
+-- SomethingElse (SCREEN)
After a successful user login, the user is sent to the Home
screen and should not be able to navigate back to the LoginScreens
screen.
I have tried to use the componentDidMount
lifecycle method on Home
, as well as the useFocusEffect
hook, with the following:
BackHandler
, returning true from the handler works (true means back action has been handled, no further back handlers will be called), but it will also block any back navigation within the screens in Home
(e.g. I cannot navigate back from Dashboard to MyProfile).navigation.reset({ index: 1, routes: [{ name: "Home" }] })
. Without index: 1
the navigation just goes back to ROOT's initialRoute (in this case, LoginScreens
). With index: 1
, a Maximum update depth exceeded
error is thrown.Home
, I have tried using a navigation.reset()
(note: no params, clears the entire navigation history), and after that navigate to the Home
screen. This doesn't achieve the desired effect since the current route (ROOT's initialRoute, in this case: LoginScreens
) is still pushed on the navigation history before navigating to Home
.Aaaaand... I have ran out of ideas. Does anyone have any suggestions ?
To make this work, you need to: Disable the swipe gesture for the screen ( gestureEnabled: false ). Override the native back button in the header with a custom back button ( headerLeft: (props) => <CustomBackButton {... props} /> ).
Use the goBack() Method to Go Back One Screen in React Native. The goBack() method is one of the most important methods in the react-navigation library. It allows you to go back to one of the previous screens in your navigation stack.
To reset the navigation stack for the home screen with React Navigation and React Native, we can use the navigation. dispatch and the CommonActions. reset methods. import { CommonActions } from "@react-navigation/native"; navigation.
It seems that React Navigation's docs tried to cover this use case with this guide:
https://reactnavigation.org/docs/en/auth-flow.html
The example there is very tricky, already introduces state management libraries, reducers, React hooks, and whatever else that doesn't really help. However, the summary of that guide is: Conditionally render routes
.
Unlinke React Navigation 4 and previous versions, in React Navigation 5 you can conditionally render routes. In doing so you effectively rule out any possibilities of navigation to an inexistent route. Below, there is a very short example of how you can do it with a simple state variable. Keep in mind however that this example only takes into account a navigator with one route rendered at a time. If you have more routes that are rendered other than the ones in this example, you may need to adjust the RootStack.Navigator
's props (initialRouteName
for example), or explicitly navigate to a specific route.
import React from "react";
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import LoginNav from "./screens/LoginNav";
import HomeScreens from "./screens/HomeScreens";
const RootStack = createStackNavigator();
export default class MyApp extends React.Component {
constructor(props){
super(props);
this.state = { isLoggedIn: false };
}
setIsLoggedIn = (isLoggedIn)=>{ this.setState({ isLoggedIn }); }
render = () => {
// Using an arrow function to allow to pass setIsLoggedIn to LoginNav
// Pass setIsLoggedIn from the props of LoginNav to the screens it contains
// then from the screens call this function with a true/false param
const LoginScreens = (props)=> <LoginNav {...props} setIsLoggedIn={this.setIsLoggedIn} />
return <NavigationContainer style={{ flex: 1 }}>
<RootStack.Navigator>
{(this.state.isLoggedIn === false)
// If not logged in, the user will be shown this route
? <RootStack.Screen name="LoginScreens" component={LoginScreens} />
// When logged in, the user will be shown this route
: <RootStack.Screen name="Home" component={HomeScreens} />
}
</RootStack.Navigator>
</NavigationContainer>;
}
}
In this example, call (this.)
props.setIsLoggedIn(true)
to render the Home
route, or call with a false
param to return to the LoginScreens
route.
Hopefully this example is easier to understand than the one in the docs.
Well, I have to admit, its was not easy to find the new reset method's syntax for v5, man... ReactNavigation docs really need an in site search functionality.
Anyway, reset method can be used, and worked perfectly for me.
It looks something like:
import { CommonActions } from '@react-navigation/native';
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [
{
name: 'Home',
params: { user: 'jane' },
},
],
})
);
I made a helper function which I am using in multiple places in my app, that looks like:
import { CommonActions } from '@react-navigation/native';
export const resetStackAndNavigate = (navigation, path) => {
navigation.dispatch(CommonActions.reset({ index: 0, routes: [{ name: path }] }));
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With