I'm using https://reactnavigation.org/ for navigation in a React Native app with a tab navigator as the main stack and a modal with two screens in it (for logging in and configuring the app).
I can't for the life of me figure out how to close the modal from the second screen (SelectItems). From the first screen in the modal I can close it with navigation.goBack().
Both modal screens need a close button. Is there a way to just return back to whatever tab the user was on?
Thanks in advance for any help.
const Tabs = TabNavigator(
{
Search: { screen: Search },
Settings: { screen: Settings }
}
);
// modal with two screens
const Setup = StackNavigator(
{
Login: {
screen: Login
},
SelectItems: {
screen: SelectItems
}
},
{
initialRouteName: 'Login'
}
);
const RootStack = StackNavigator(
{
Main: {
screen: Tabs
},
Setup: {
screen: Setup
}
},
{
mode: 'modal',
headerMode: 'none'
}
);
I found a solution but it isn't perfect.
You can use the popToTop which will go back to the first Scene of your stack and than the goBack will close the modal.
navigation.popToTop();
navigation.goBack(null);
The problem with that is that it will mount again the first scene of the stack, so be sure you dont use setState in you willMount or didMount. Or prevent it.
That's the solution i'm going with for now. I keep looking for a better solution.
Simple and easy solution for react-navigation 5.x (getParent docs):
navigation.getParent()?.goBack();
This works because it grabs the navigator's parent, which is the modal and what you want to dismiss.
NOTE: In older versions of 5.x this was called dangerouslyGetParent. That exists in newer 5.x versions, but is now deprecated. Use that if getParent isn't available in the version of react-navigation that you're using. It isn't actually dangerous: From react-navigation's documentation:
Reason why the function is called dangerouslyGetParent is to warn developers against overusing it to eg. get parent of parent and other hard-to-follow patterns.
This was my solution with v6 in 2022. It closes the modal and navigates away without any weird behaviors (at least in my case).
onPress = () => {
navigation.goBack(); // <-- this fixed it
navigation.navigate("SomeScreen", { id: 123});
}
If you use react-navigation 4.x there is a method navigation.dismiss(). The method dismisses the entire stack and return to the parent stack
https://reactnavigation.org/docs/4.x/navigation-prop/#dismiss
If you are using Stack Navigation you can always move around in the navigation stack using navigation.pop(). For instance, if you want to close two open modals you can call the pop function with parameter value 2:
navigation.pop(2);
Original solution from https://github.com/react-navigation/react-navigation/issues/686#issuecomment-342766039, updated for React Navigation 4:
Create a DismissableStackNavigator:
import React from 'react';
import { createStackNavigator } from 'react-navigation-stack';
export default function DismissableStackNavigator(routes, options) {
const StackNav = createStackNavigator(routes, options);
const DismissableStackNav = ({navigation, screenProps}) => {
const { state, goBack } = navigation;
const props = {
...screenProps,
dismiss: () => goBack(state.key),
};
return (
<StackNav
screenProps={props}
navigation={navigation}
/>
);
}
DismissableStackNav.router = StackNav.router;
return DismissableStackNav;
};
Usage:
Creating your stack:
// modal with two screens
const Setup = StackNavigator(
{
Login: Login,
SelectItems: SelectItems
},
{
initialRouteName: 'Login'
headerMode: 'none'
}
);
Call navigation.dismiss in your screens to close the modal stack.
I was trying to figure this myself and the solution I ended up using was to use navigation.navigate()
Example this.props.navigation.navigate('name of screen you want to go');
Hope this helps!
Related
I build an app with react-native, expo and react-navigation. I have a main drawer navigator who has in it 2 other stack navigator. One stack navigator have 2 pages; Products and Product. When I click on one product in the Products page, it goes into the Product page. Ben if I click on another link in my drawer navigator, I would like that the Products page's stack navigator would return to it's initialRoute when leaving the stack navigator.
I tried to set the initialState for when I exit the stack navigator, it render the initialState when I click on the navigator link in my drawer navigator, but it doesn't work when I'm in the child page and exit the navigator. When I click again on Products in my drawer, instead of navigate to the Products page, it stayed on Product.
I can create statics links in my drawer navigator and use this.props.navigation.navigate to always go to this.props.navigation.navigate('Products'), but it will be the last thing I want. I would really love that my drawer navigator stays dynamic with what I pass to it.
I tried to this.props.navigation.navigate when the componentWillUnmountlifecycle goes on, but it didn't work.
How can I do this?
Thank you for your help!
Yes it is entirely possible to reset the stack with react-navigation, there is a specific action for this.
Here is an example when the drawer is in a tab navigation.
First we define our drawer in MenuDrawer.js, nothing special here:
import { createDrawerNavigator } from 'react-navigation';
import ProductStack from './ProductStack';
import OtherStack from './OtherStack';
const MenuDrawer = createDrawerNavigator({
Product: {
screen: ProductStack,
navigationOptions: {
drawerLabel: 'My products',
},
},
Other: {
screen: OtherStack,
navigationOptions: {
drawerLabel: 'Something else',
},
},
});
export default MenuDrawer;
Finally we define our tab navigation, and we use the tabBarOnPress navigation option to listen to any drawer opening and reset the stack if needed:
import { createBottomTabNavigator, StackActions, NavigationActions } from 'react-navigation';
import MenuDrawer from './MenuDrawer';
const BottomTabs = createBottomTabNavigator({
Menu: {
screen: MenuDrawer,
navigationOptions: {
title: 'Menu',
tabBarOnPress: ({ navigation }) => {
const notResetRoute = navigation.state.routes.find(x => x.index > 0); // Check for stack not positioned at the first screen
if (notResetRoute) {
const resetAction = StackActions.reset({ // We reset the stack (cf. documentation)
index: 0,
actions: [
NavigationActions.navigate({ routeName: notResetRoute.routes[0].routeName }),
],
});
navigation.dispatch(resetAction);
}
navigation.openDrawer(); // Finally we open the drawer
},
},
},
});
export default BottomTabs;
Here the drawer is opened when the user clicks on the corresponding tab. Obviously the same could be made if the drawer is opened differently, for instance through a click on a button in a given screen. The important thing is to reset the stacks at the same time.
import { NavigationActions } from 'react-navigation'
this.props.navigation.dispatch(NavigationActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: 'ParentStackScreen' })]
}))
I had to refactor recently the navigation inside my React Native App using React Navigation.
Indeed, I have several navigators inside my app for different purposes: screens when not connected, screens when connected and some screens that should be available in both situations
(e.g. terms of use).
So my question is the following, if I have this pattern, how can I for example navigate from TermsOfUse to Register without going back to Welcome?
I can't use navigate.goBack() or navigation.navigate('Register') since those screens are not in the same StackNavigator, and duplicate the TermsOfUse in both navigators would be quite dirty.
// Screens
const NotConnectedScreens = {
Welcome: { screen: WelcomeScreen },
Register: { screen: RegisterScreen },
}
const ConnectedScreens = {
Homepage: { screen: HomepageScreen },
Tutorial: { screen: TutorialScreen },
}
const OthersScreens = {
TermsOfUse: { screen: TermsOfUseScreen },
}
// Stacks
const NotConnectedStack = createStackNavigator(NotConnectedScreens, {
initialRouteName: 'Welcome',
})
const ConnectedScreens = createStackNavigator(ConnectedScreens, {
initialRouteName: 'Homepage',
})
const OtherStack = createStackNavigator(OtherScreens, {
initialRouteName: 'TermsOfUse',
})
// App navigation
const AppContainer = createAppContainer(
createSwitchNavigator(
{
NotConnectedStack: NotConnectedStack,
ConnectedStack: ConnectedStack,
OthersStack: OthersStack,
},
{
initialRouteName: 'LandingStack',
defaultNavigationOptions: {
header: null,
headerMode: 'none',
},
}
)
)
export { AppContainer as default }
I can't use navigate.goBack() or navigation.navigate('Register') since
those screens are not in the same StackNavigator.
Not completely true, if your screen has a unique name, wherever you need to navigate to that page you can call the function this.props.navigation.navigate('SecondPage').
but sometimes going to an screen from another stack can't be done because it needs some data to be passed when navigating to. in this case MAKE SURE YOUR SCREEN HAS A DIFFERENT NAME
DevicesList: {screen: DevicesList },// this is wrong
DevicesListPage: {screen: DevicesList },// this is right
then you can navigate to that page like below from another stack:
this.props.navigation.navigate('DevicesListPage', {
//your data
});
I am having an issue when I navigate from Home component that contains a list of companies and I press in a button to load the Company view, the data is being loaded but when I press back in Android is returning to Home and when I press in a different company button to load its details, is rendering the same view with the same previous data, that means, the component is not being updated/unmounted.
These are my routes
const drawerConfig = {
initialRouteName: 'Home',
contentComponent: SideMenu,
drawerWidth: width,
}
const MainDrawerNavigator = createDrawerNavigator(
{
Home: {
screen: Home,
},
Company: {
screen: Company,
},
Gifts: {
screen: Gifts,
},
Contact: {
screen: Contact
}
},
drawerConfig,
);
const InitialStack = createStackNavigator(
{
Menu: {
screen: Menu,
path: 'menu/',
}
},
{
initialRouteName: 'Menu',
headerMode: 'none',
}
);
const SwitchNavigator = createSwitchNavigator(
{
Init: InitialStack,
App: MainDrawerNavigator,
},
{
initialRouteName: 'Init',
}
);
const AppContainer = createAppContainer(SwitchNavigator);
export default AppContainer;
I am navigating from Home to Company with this
goToCompany = company => event => {
this.props.navigation.navigate('Company', {
company,
});
}
And receiving the params in Company with this
componentWillMount() {
this.setState({ companyData: this.props.navigation.getParam('company', {}) });
}
So I am expecting the Company component will unmount on pop or allow me to change the state of the Company component when I send the details from Home.
I am using react-navigation 3.5.1 and react-native 0.59.3
React native navigation does not work as web. Read here for more details. https://reactnavigation.org/docs/en/navigation-lifecycle.html
When you navigat to other screen it doesn't actually get unmounted. Read documentation for details.
Use willFocus instead.
you can try using componentDidUpdate check in docs
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
You need to use the push method of navigation object. The push replace the current route with a new and the navigate search a route and if not exist create new one.
I am using TabNavigator from react-navigation. It's working fine but I need to do a little trick.
When I navigate between StackNavigator routes, after changing tabs I need my route go directly in the initial route. So I need the route state to be reset.
const HomeStack = StackNavigator({
Main: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
});
const AboutStack = StackNavigator({
Main: { screen: AboutScreen },
});
TabNavigator(
{
Home: { screen: HomeStack },
About: { screen: AboutStack },
}
Let's say I am in the Main route from the Home tab and then I have navigated to Profile before switching to the About tab. When I go back to the Home tab I want my app directly to navigate to the Main route and clear history. Just like a reset.
Any suggestion ?
You maybe could use the willFocus listener in the Profile route of your HomeStack.
Listener:
class Profile extends Component {
componentDidMount() {
this.didFocusListener = this.props.navigation.addListener(
'didFocus',
() => { console.log('did focus') },
);
}
componentWillUnmount() {
this.didFocusListener.remove();
}
render() {
return ( /* your render */ );
}
}
And then in the case you are on Main and you are navigating to your Profile route. You should set a params in the navigation to say that the previous route is Main.
Route parameters: navigation.navigate('Profile', { previous_screen: 'Main' });
So now in your willFocus listener:
if the previous_screen param is set, it means that you don"t have to do anything.
If not, means that you come from the other tab and that it will navigate to the wrong route. So you can either reset you're navigation route or just navigate to the 'Profile' route.
NOTE:
I didn't try this solution and maybe the transition animation is not going to be smooth. So tell me if it does the job well or not.
I have a problem that I haven't been able to solve.
In my React native application, I would like to display a welcome screen at the start. Then 5 seconds later just close it, and display another one. Both are 2 entirely different screens, no need to keep the "come back" arrow.
I have been searching for hours, but I haven't found out how to do it.
Here is my code for now:
import Defis from './components/defis'
import Quote from './components/quote'
export default class Betty extends Component {
componentDidMount(){
// Start counting when the page is loaded
this.timeoutHandle = setTimeout(()=>{
// Add your logic for the transition
this.props.navigation.navigate('Defis') // what to push here?
}, 5000);
}
componentWillUnmount(){
clearTimeout(this.timeoutHandle);
}
render() {
return (
<Quote/>
);
}
}
Does anybody know how to do it?
I'm not able to use Navigator.push, moreover Navigator seems deprecated.
Not Using any navigator this can solve your problem
import Defis from './components/defis'
import Quote from './components/quote'
export default class Betty extends Component {
constructor(props){
super(props)
this.state = {
component : <Quote />
}
}
componentDidMount(){
// Start counting when the page is loaded
this.timeoutHandle = setTimeout(()=>{
// Add your logic for the transition
this.setState({ component: <Defis /> })
}, 5000);
}
componentWillUnmount(){
clearTimeout(this.timeoutHandle);
}
render() {
return (
this.state.component
);
I have done this to show login screen after the splash screen in react-native as follows:
import Login from './Login'; // My next screen
....
....
const {navigate} = this.props.navigation;
setTimeout(() => {
navigate('Login'); //this.props.navigation.navigate('Login')
}, 5000); //5000 milliseconds
I have used react-navigation for the navigation purpose.
I was doing almost the same thing with "react-native-router-flux".
Simply render a first screen, in your case the "Quote", and then set in componentDidMount:
setTimeout(() => {
Actions.yourNextSceneName()
}, milliseconds)
Hope this helps.
This worked for me:
import { NavigationActions } from "react-navigation";
componentDidMount(){
setTimeOut( () => {
NavigationActions.navigate('login');
}, 5000 );
}
You can do it with using navigator by returning a View with the onLayout prop and adding the setTimeout function to the prop.
How to navigate to another screen after timeout in React Native:
So I have created my navigation structure and the respective pages already.
Using functional component in ReactNative, do this this to the componentthat you want navigate from:
function MyPresentScreen( {navigation}, props ){
setTimeout(() => {
navigation.navigate('MyTargetScreen');
}, 2500);
return(
<Text>My Present Screen that I will navigate from after some seconds</>
)
};
Note that you can customize the timeout as you wish. The above is 2 and half seconds.
Credit: Even though it was written with class component, this article was helpful in helping me figure this out: