Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to navigate from nested StackNavigator to parent's router screen? #335

Closed
goncalomarques opened this issue Feb 14, 2017 · 51 comments
Closed
Labels

Comments

@goncalomarques
Copy link

@goncalomarques goncalomarques commented Feb 14, 2017

I have several nested navigators with the following structure:

StackNavigator
    Home
    Login
    Authenticated -> CustomTabNavigator
        ScreenOne -> StackNavigator
            ScreenOneDetails
            ScreenOneSettings
        ScreenTwo

How can I navigate from ScreenOneDetails to Home?

Already tried to dispatch it as an action:

const resetAction = NavigationActions.reset({
    index: 0,
    actions: [
        NavigationActions.navigate({ routeName: 'Home' })
    ]
});
this.props.navigation.dispatch(resetAction);
this.props.navigation.goBack(null);

But all I get is an error - There is no route defined for key splash. Must be one of: 'ScreenOneDetails','ScreenOneSettings'

I know that this makes sense, as the navigation prop that is passed to ScreenOneDetails is the one created by the ScreenOne StackNavigator. However, is there any way to access the navigation prop created by parent navigators? And how would I go the other way around, e.g: from Login to ScreenOneSettings?

@nico1510
Copy link

@nico1510 nico1510 commented Feb 17, 2017

First off, I'm having the same questions but here's an idea what you could try:

And how would I go the other way around, e.g: from Login to ScreenOneSettings?

The docs include an example I think. Specifically:

  • action - (advanced) The sub-action to run in the child router, if the screen is a navigator.

So you would call something like:

navigate('ScreenOne', {}, NavigationActions.navigate({ 'ScreenOneSettings' }));

As to your first problem:

However, is there any way to access the navigation prop created by parent navigators?

have you tried passing down the navigation prop from the root StackNavigator as a prop down to your child StackNavigator ? Technically I think this is how it should work (by reading this section of the docs), but in practice I got some errors when trying this.

@goncalomarques
Copy link
Author

@goncalomarques goncalomarques commented Feb 17, 2017

Thanks @nico1510

I had already tried that before creating this issue, but today I managed to make it work. I still have some issues related to the CustomTabNavigator navigationOptions, as I'm passing a TabsNavigator and want to keep the top navbar dynamically linked to the tabs pages

@zoontek
Copy link

@zoontek zoontek commented Feb 17, 2017

@goncalomarques How did you make it work? I've exactly the same problem.

@goncalomarques
Copy link
Author

@goncalomarques goncalomarques commented Feb 17, 2017

@zoontek I created wrapper components that I use in the screen property for a route I set. In these wrapper components, I pass down the navigation to the nested router in the screenProps prop as whatever I want to call it, in this case rootNavigation:

const NestedNavigator = StackNavigator({
    ScreenOne: {
        screen: ScreenOneComponent
    },
    ScreenTwo: {
        screen: ScreenTwoComponent
    }
});

class NestedNavigatorWrapper extends React.Component {
    constructor(props)  {
        super(props);
    }
    render() {
        return (
            <DashboardNavigator screenProps={{ rootNavigation: this.props.navigation }} />
        );
    }
}
const AppNavigator = StackNavigator({
    Home: {
        screen: HomeComponent
    },
    Nested: {
        screen: NestedNavigatorWrapper
    }
});

To use navigate('Home') inside ScreenOne, I use this.props.rootNavigation.navigation.navigate('Home').

I don't think this is the optimal solution, just a quick fix.

@temitope
Copy link

@temitope temitope commented Feb 17, 2017

I had this issue and found that the ideal way to navigate between nested navigators is via the dispatch method where you can have a nested sub-navigation action. @zoontek @goncalomarques


                this.props.navigation.dispatch(
                  {
                      type: 'Navigation/NAVIGATE',
                      routeName: 'GoToANavigator',
                      action: {
                        type: 'Navigation/NAVIGATE',
                        routeName: 'GoToAScreenInANavigator',
                      }
                  }
                 );
@koenpunt
Copy link

@koenpunt koenpunt commented Feb 24, 2017

This works totally fine if you want to go back from a nested navigator:

navigation.dispatch({ type: 'Navigation/BACK' })

Edit this works when on the first page of the nested navigator, so not a complete solution..

@bjrn
Copy link

@bjrn bjrn commented Mar 3, 2017

I think I've bumped into the same issue as you. The about navigation.dispatch works for me too, but I found an interesting section in the documentation:

[..] If the goal is to go back anywhere, without specifying what is getting closed, call .goBack(null);

navigation.goBack(null); works at least in my use case.

@Kerumen
Copy link

@Kerumen Kerumen commented Mar 11, 2017

Just a heads up for @goncalomarques answer:

You can inline the nested navigator instead of making it a class:

const AppNavigator = StackNavigator({
  Home: { screen: HomeComponent },
  Nested: { screen: ({ navigation }) => <DashboardNavigator screenProps={{ rootNavigation: navigation }} /> }
});

And the right call to navigate is: this.props.screenProps.rootNavigation.navigate('Home').

@kylecrouse
Copy link

@kylecrouse kylecrouse commented Mar 29, 2017

@Kerumen When using your example and dispatching a navigate action to a parent navigator from one nested navigator and including a sub-action to navigate to the non-initial screen of a sibling nested navigator, the sub-action appears to be ignored.

Example navigators:

const MainNavigator = StackNavigator({
  screenOne: {
    screen: MainOneComponent
  },
  screenTwo: {
    screen: MainTwoComponent
  },
};

const NestedNavigator = StackNavigator({
  screenOne: {
    screen: NestedOneComponent
  },
  screenTwo: {
    screen: NestedTwoComponent
  },
};

const AppNavigator = StackNavigator({
  main: {
    screen: ({ navigation, screenProps }) => <MainNavigator screenProps={{ parentNavigation: navigation, ...screenProps }} />
  },
  nested: {
    screen: ({ navigation, screenProps }) => <NestedNavigator screenProps={{ parentNavigation: navigation, ...screenProps }} />
  },
});

Example dispatch action from header of one of the nested navigator's child components:

class MainOneComponent extends Component {
  static navigationOptions = {
    header: ({ state }) => ({
      right: (
        <Button onPress={() => {
          let navigationAction = NavigationActions.navigate({
            routeName: 'nested',
            action: NavigationActions.navigate({ routeName: 'screenTwo' })
          });
          state.params.parentNavigation.dispatch(navigationAction);
        }}/>
      )
    })
  }
...
}

The resulting screen displayed is nested->screenOne, not nested->screenTwo as expected.

If AppNavigator is modified to include NestedNavigator directly instead of via function, screenTwo displays as expected but I lose the ability to call the parentNavigator since it isn't getting passed down as a screenProp.

This results in the correct transition (but lacks parentNavigation prop in nested's children):

const AppNavigator = StackNavigator({
  main: {
    screen: ({ navigation, screenProps }) => <MainNavigator screenProps={{ parentNavigation: navigation, ...screenProps }} />
  },
  nested: {
    screen: NestedNavigator
  },
});

I tried another approach where I passed navigation as a prop on the NestedNavigator component within the function, but that resulted in an error that I haven't yet been able to trace:

const AppNavigator = StackNavigator({
  main: {
    screen: ({ navigation, screenProps }) => <MainNavigator screenProps={{ parentNavigation: navigation, ...screenProps }} />
  },
  nested: {
    screen: ({ navigation, screenProps }) => <NestedNavigator navigation={navigation} screenProps={{ parentNavigation: navigation, ...screenProps }} />
  },
});

image

Any thoughts on what I might be missing that will pass the sub-action through properly?

@Kerumen
Copy link

@Kerumen Kerumen commented Mar 30, 2017

@kylecrouse I stumbled upon the same problem..
Didn't manage to fix it so I reverted my code and declared the screen directly with the component:

const AppNavigator = StackNavigator({
  Nested: { screen: DashboardNavigator }
});
@InteractiveLogic
Copy link

@InteractiveLogic InteractiveLogic commented Apr 3, 2017

Hmmm. I've got a bit of a catch-22 here it seems. @goncalomarques -- your solution (and @kylecrouse's) works for me as far as the navigation is concerned but I'm finding that when I do the initial dispatch to navigate to the nested TabNavigator and it's target screen, the params are getting squashed or not passed on.

It's super frustrating.

I think I can get around it by using redux instead of the params, but it's a bit of a hassle for such a small amount of data (one string).

The good news is that calling dispatch() with a reset action on the outer navigation does pop everything back to where I want it. I'm concerned about other side effects too, but this gives me a little hope.

UPDATE: using redux worked fine in my case to get the data to the screen in the nested tab navigator. I'm not sure what other side effects I'll run into on the other screens, but at least I have that working for now.

@ramirobg94
Copy link

@ramirobg94 ramirobg94 commented May 12, 2017

I have also spent hours on this problem, thank you with the idea of using wrapper components.

I have found the following fix using Redux.

//reducerX.js

...
import { NavigationActions } from 'react-navigation';
...
function nav(state=inS, action){
  let nextState;
  switch (action.type) {
      case 'Home':
       homeAction = AppNavigator.router.getActionForPathAndParams('Home');
       nextState = AppNavigator.router.getStateForAction(homeAction);
       break;
    default:
      nextState = AppNavigator.router.getStateForAction(action, state);
      break;
  }
  return nextState || state;
}

....

//nestedComponent
<Button
      onPress={() => this.props.screenProps.parentNavigation.dispatch({ type: 'Home' })}
      title="Home"
    />

I hope it is useful.

@lionhive
Copy link

@lionhive lionhive commented May 24, 2017

I have a similar problem with Presentational/Container ocmponents and navigating.
#1675
What is down side of dispatching actions?

@manuTro
Copy link

@manuTro manuTro commented Jun 22, 2017

Is it possible to nest just stacknavigator?
If so, How can I navigate between them?

@crazyx13th
Copy link

@crazyx13th crazyx13th commented Jul 13, 2017

Sorry one question... nesting my Navigation is a good solution for now (later I will use Redux-Style), but now the MainScreenNavigator.navigationOptions = { block not working. how can I insert the navigationOptions for the inner MainScreenNavigator ? thx!

 const MainScreenNavigator = TabNavigator({
            Redeem: {screen: RedeemNavigator},
            Statistics: {screen: StatisticsNavigator},
            Settings: {screen: SettingsNavigator},
        });
        MainScreenNavigator.navigationOptions = {
            header: null,
            gesturesEnabled: false
        };

        const RootNavigator = StackNavigator({
            Login: {screen: LoginScreen},
            MainNavigator: {screen: ({ navigation, screenProps }) => <MainScreenNavigator screenProps={{parentNavigation: navigation, ...screenProps }} />}
        }, {
            initialRouteName: 'Login'
        });
@prithsharma
Copy link

@prithsharma prithsharma commented Jul 13, 2017

Solutions by @goncalomarques @Kerumen work well for passing rootNavigation from a screen in the root navigator to a nested navigator. While this is a good solution for being able to access rootNavigation.dispatch from screens in a nested navigator.
However, I observed that this doesn't help in passing the rootNavigationState to a nested navigator. That is because every screen of a navigator receives a navigation prop, the state key in this prop contains state of only that screen.

Anyone looking here for a way to pass rooNavigationState might benefit from the following way -

const RootNavigator = StackNavigator({
  screen1: screenNavigator1,
  screen2: screenNavigator2,
});

class RootNavigatorWrapper extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      rootNavState: RootNavigator.router.getStateForAction(NavigationActions.INIT),
    };
  }

  render() {
    return (
      <RootNavigator
        screenProps={{
          rootNavState: this.state.rootNavState,
        }}
        onNavigationStateChange={(prevState, nextState) => {
          this.setState({
            rootNavState: nextState,
          });
        }}
      />
    );
  }
}

With this way, the nested screen navigators(screenNavigator1 and screenNavigator2) would now receive a prop that has the navigation state of the root navigator.

@BenoitClaveau
Copy link

@BenoitClaveau BenoitClaveau commented Jul 20, 2017

@ramirobg94 how do you do to set the initialState of the reducer ?

@cadday
Copy link

@cadday cadday commented Jul 21, 2017

import { NavigationActions } from 'react-navigation'

const navigateAction = NavigationActions.navigate({
  routeName: 'Profile',
  params: {},

  // navigate can have a nested navigate action that will be run inside the child router
  action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
})
this.props.navigation.dispatch(navigateAction)
// this button is in teams screen and can navigate to loading
<Button title="TO LOADING PAGE"
          onPress={() => {
            const navigateAction = NavigationActions.navigate({
              routeName: "Loading",
              params: {}
            });
            this.props.navigation.dispatch(navigateAction);
          }}
        >
        </Button>


//nested screens
const TeamsStack = StackNavigator({
  Teams: { screen: Teams }
});

const ProfileStack = StackNavigator({
  Profile: { screen: Profile }
});

const NotificationsStack = StackNavigator({
  Notifications: { screen: Notifications }
});

const DrawerNav = DrawerNavigator({
  Main: { screen: TeamsStack },
  Profile: { screen: ProfileStack },
  NotificationsStack: { screen: NotificationsStack }
});

const BackgroundColors = StackNavigator(
  {
    Loading: { screen: LoadingScreen },
    Auth: { screen: Auth },
    Main: { screen: DrawerNav }
  },
  {
    headerMode: "none"
  }
);

https://reactnavigation.org/docs/navigators/navigation-prop#dispatch-Send-an-action-to-the-router

able to navigate from child to parent. for example from teams screen to loading. have not tried parent to child.
this worked for me.

@danstepanov
Copy link

@danstepanov danstepanov commented Jul 27, 2017

@goncalomarques Shouldn't DashboardNavigator be NestedNavigator?

@dzpt
Copy link

@dzpt dzpt commented Aug 10, 2017

@goncalomarques I'm in the same situation like you. One wrapper for TabNavigator, the root is StackNavigator
what if i want navigate from Home (root) to ScreenTwo of Nested ?

@spencercarli
Copy link
Member

@spencercarli spencercarli commented Aug 28, 2017

Hi! In an effort to get the hundreds of issues on this repo under control I'm closing issues tagged as questions. Please don't take this personally - it's simply something we need to do to get the issues to a manageable point. If you still have a question I encourage you to re-read the docs, ask on StackOverflow, or ask on the #react-navigation Reactiflux channel. Thank you!

@manjeetwadi
Copy link

@manjeetwadi manjeetwadi commented Dec 19, 2017

@crazyx13th thanks for the response, actually I tried this way and it works perfectly but we lost control to navigationOptions at screen level in that approach (like try to set tabBarVisible:false)

@e-nouri
Copy link

@e-nouri e-nouri commented Dec 29, 2017

@temitope I think the cleanest solution I found is to set the initialRouteName params and then to call the head of the nested stack and it will route automatically to the initialRouteName defined earlier. In my case this is what i used because I split my Screens "Tree" in 2 branches, beforeAuth and afterAuth. Here is an example:

const afterAuthRoutes = {
    Home: {
        screen: HomeScreenStack
    },
    History:{
        screen: AfterAuthScreens.HistoryScreen
    },
...
}

const TabConfig = {
    initialRouteName: 'Home',
...
}

const afterAuth = TabNavigator(afterAuthRoutes, TabConfig)


const beforeAuthRoutes = {
    Login: {
        screen: AuthScreens.LoginScreen,
        path: 'auth/login'
    },
    Signup: {
        screen: AuthScreens.SignUpScreen,
        path: 'auth/signup'
    },
...
}

const authConfig = {
     initialRouteName: 'Login',
...
}

const beforeAuth = StackNavigator(beforeAuthRoutes, authConfig)

const AppNav = StackNavigator({
    authStack: { screen: beforeAuth },
    mainStack: { screen: afterAuth }
  }, {
    // Default config for all screens
    headerMode: 'none',
    title: 'Main',
    initialRouteName: 'authStack'
  })


And now to login the user, I just call navigate("mainStack") to logout the user, navigate("authStack"), ofc by logout and login I mean changing the screens, I am not talking about the entire process.

Hope this will help.

@walter211
Copy link

@walter211 walter211 commented Jan 2, 2018

@mustafatavbatir how do you control the drawer?

@cadday
Copy link

@cadday cadday commented Jan 2, 2018

@walter211 switched to react-native-navigation long time ago, sorry:(

@ahmad2smile
Copy link

@ahmad2smile ahmad2smile commented Jan 10, 2018

@goncalomarques I was just about to kill myself. thanks your solution worked.

@andrekovac
Copy link

@andrekovac andrekovac commented Jan 11, 2018

@Kerumen but when you reverted your code to have sub-actions working again, the rootNavigation is not passed down anymore and thus the problem of this question is not fixed.. How did you go about that then?

@uri3000
Copy link

@uri3000 uri3000 commented Jan 21, 2018

@Kerumen this line is not working for me in the nested navigator - this.props.screenProps.rootNavigation.navigate('Home')

I managed to get the call working only when calling the screenProps directly, without a preceding this.props: screenProps.rootNavigation

Could anyone please explain why it only works this way?

I haven't defined a new component though, this is how I passed through the rootNavigator:

const SignupWizard = StackNavigator({
  SignUpPersonalDetails: {
    screen: SignUpPersonalDetails
  },
  SignUpLinkAccount: {
    screen: SignUpLinkAccount,
  },
});

and then in render():
<SignupWizard screenProps={rootNavigation = this.props.navigation} />

@shekharskamble
Copy link

@shekharskamble shekharskamble commented Feb 27, 2018

@goncalomarques - How do I pass extra props to NestedNavigatorWrapper? besides root navigation I need to pass extra data to DashboardNavigator from AppNavigator Please help

@Nickersoft
Copy link

@Nickersoft Nickersoft commented Mar 6, 2018

@uri3000 screenProps is supposed to be an object. All you're doing in that code is assigning a value to the rootNavigation variable. It should look like this:

<SignupWizard screenProps={{ rootNavigation: this.props.navigation }} />
@brentvatne
Copy link
Member

@brentvatne brentvatne commented Mar 6, 2018

we're going to add an escape hatch for this with this.props.navigation.dangerouslyGetParent() -- more info at react-navigation/rfcs#27

@peni4142
Copy link

@peni4142 peni4142 commented Apr 6, 2018

My Solution to resolve all accessing problems:

NavigationService = null;

export function getNavigationService() {
    if (this.NavigationService == null) {
        this.NavigationService = new NavigationService();
        return this.NavigationService;
    }
    else {
        return this.NavigationService;
    }
}

class NavigationService {
    constructor() {

    }
    navigations = {};

    addNavigation(key, navigation){
        if(navigation[key] === undefined){
            this.navigations[key] = navigation;
        }
        else{
            //console.log(key  + ' already exists in navigation')
        }
    }

    navigate(nameOfNavigation, navigateTo, params){
        console.log(nameOfNavigation + '\n' + navigateTo + '\n' + params)
        navigation = this.navigations[nameOfNavigation];
        if(params == null | params == undefined){
            navigation.navigate(navigateTo);
        }
        else{
            navigation.navigate(navigateTo, params);
        }
        
    }
    
} 

@keech
Copy link

@keech keech commented Apr 15, 2018

@peni4142 I would like to know how to use this. Could you give more information?

@microcipcip
Copy link

@microcipcip microcipcip commented Apr 30, 2018

The solutions above are overly complicated! If you are using Redux, you can store a reference with a middleware and use it anywhere, no matter the nesting...

STEP 1: create a redux storeRef middleware

// storeRefHandler.js
import { refStore } from './store' // refStore is just an object...

export default () => {
  return next => action => {
    switch (action.type) {
      case 'STORE_REF':
        refStore[action.refName] = action.ref
        break
    }
    return next(action)
  }
} 

STEP 2: add the middleware to your redux store

// store.js
import refHandler from './storeRefHandler'
const store = createStore(
  rootReducer,
  applyMiddleware(refHandler),
)

// export the refStore object so that it can be accessed anywhere
export const refStore = {} 

STEP 3: create the action

// actions.js
export const storeRef = ({ ref, refName }) => ({
  type: 'STORE_REF',
  refName,
  ref,
})

STEP 4: there is no step 4, you are done!

Usage:

// first we store the navigation we want to use, for example from our MyRootComponent....
import { connect } from 'react-redux'
import { storeRef } from './actions'
componentDidMount () {
  this.props.storeRef({
    refName: 'rootNavigation',
    ref: this.props.navigation
  })
}

function mapDispatchToProps (dispatch) {
  return { storeRef: ({ ...props }) => dispatch(storeRef({...props})) }
}
export default connect(null, mapDispatchToProps)(MyRootComponent)

// now we can use it anywhere!
import { refStore } from 'src/store'
class MyAwesomeComponent extends Component {
  render () {
    return (
      <Button
        onPress={() => refStore.rootNavigation.navigate('Welcome')}
        title="Go to Welcome screen"/>
    )
  }
}
@Friendly-Robot
Copy link

@Friendly-Robot Friendly-Robot commented May 12, 2018

                this.props.navigation.dispatch(
                  {
                      type: 'Navigation/NAVIGATE',
                      routeName: 'GoToANavigator',
                      action: {
                        type: 'Navigation/NAVIGATE',
                        routeName: 'GoToAScreenInANavigator',
                      }
                  }
              );

This method works but because it's not dispatched from within a NavigationActions.reset(), the StackNavigator still affects the goBack() functionality that is expected of the DrawerNavigator.

In order to address the issue of removing the Navigator from the Stack using reset, try navigating to the nested route inside of a reset():

    const resetAction = NavigationActions.reset({
      index: 0,
      actions: [NavigationActions.navigate({ 
        routeName: 'DrawerNavigator',
        action: {
          type: 'Navigation/NAVIGATE',
          routeName: 'NestedRoute',
        }
      })]
    })
    this.props.navigation.dispatch(resetAction)
@tiann
Copy link

@tiann tiann commented Jun 1, 2018

dangerouslyGetParent() can get the parent navigator's navigation property.

StackNavigator
    Home
    Login
    Authenticated -> CustomTabNavigator
        ScreenOne -> StackNavigator
            ScreenOneDetails
            ScreenOneSettings
        ScreenTwo

If you want to ScreenOneDetails -> Login,

this.props.navigation.dangerouslyGetParent().dangerouslyGetParent().navigate('Login')

@adarshashokplr
Copy link

@adarshashokplr adarshashokplr commented Jul 13, 2018

I found an easy solution
use withNavigation is a higher order component which passes the navigation prop into a wrapped Component.

example : https://stackoverflow.com/a/51333660/2849146

for more details : https://reactnavigation.org/docs/en/connecting-navigation-prop.html

@fanlion
Copy link

@fanlion fanlion commented Oct 29, 2018

Navigating without the navigation prop。also can resolve this question。

Calling functions such as navigate or popToTop on the navigation prop is not the only way to navigate around your app. As an alternative, you can dispatch navigation actions on your top-level navigator, provided you aren't passing your own navigation prop as you would with a redux integration. The presented approach is useful in situations when you want to trigger a navigation action from places where you do not have access to the navigation prop, or if you're looking for an alternative to using the navigation prop.

You can get access to a navigator through a ref and pass it to the NavigationService which we will later use to navigate. Use this only with the top-level (root) navigator of your app.

// App.js

import NavigationService from './NavigationService';

const TopLevelNavigator = createStackNavigator({ /* ... */ })

class App extends React.Component {
  // ...

  render() {
    return (
      <TopLevelNavigator
        ref={navigatorRef => {
          NavigationService.setTopLevelNavigator(navigatorRef);
        }}
      />
    );
  }
}

In the next step, we define NavigationService which is a simple module with functions that dispatch user-defined navigation actions.

 // NavigationService.js

import { NavigationActions } from 'react-navigation';

let _navigator;

function setTopLevelNavigator(navigatorRef) {
  _navigator = navigatorRef;
}

function navigate(routeName, params) {
  _navigator.dispatch(
    NavigationActions.navigate({
      routeName,
      params,
    })
  );
}

// add other navigation functions that you need and export them

export default {
  navigate,
  setTopLevelNavigator,
};

Then, in any of your javascript modules, just import the NavigationService and call functions which you exported from it. You may use this approach outside of your React components and, in fact, it works just as well when used from within them.

  // any js module
import NavigationService from 'path-to-NavigationService.js';

// ...

NavigationService.navigate('ChatScreen', { userName: 'Lucy' });

In NavigationService, you can create your own navigation actions, or compose multiple navigation actions into one, and then easily reuse them throughout your application. When writing tests, you may mock the navigation functions, and make assertions on whether the correct functions are called, with the correct parameters.

@mattwills8
Copy link

@mattwills8 mattwills8 commented Jan 31, 2019

Just a heads up for @goncalomarques answer:

You can inline the nested navigator instead of making it a class:

const AppNavigator = StackNavigator({
  Home: { screen: HomeComponent },
  Nested: { screen: ({ navigation }) => <DashboardNavigator screenProps={{ rootNavigation: navigation }} /> }
});

And the right call to navigate is: this.props.screenProps.rootNavigation.navigate('Home').

This looked perfect, but it doesnt work inside a switch navigator. Any ideas for that? :/

@n0umankhan
Copy link

@n0umankhan n0umankhan commented Feb 9, 2019

just in your onPress method

onPress={() => {
    this.props.navigation.dispatch(StackActions.popToTop());
    this.props.navigation.navigate("routename", {
         title: "pass title param if need" 
     });       
}}

This will clear your route history navigate to your desired route and when you press back it will redirect to initial screen of the tab :)

@jdnichollsc
Copy link

@jdnichollsc jdnichollsc commented Feb 12, 2019

@jrrcdev
Copy link

@jrrcdev jrrcdev commented Feb 13, 2019

Navigating without the navigation prop。also can resolve this question。

Calling functions such as navigate or popToTop on the navigation prop is not the only way to navigate around your app. As an alternative, you can dispatch navigation actions on your top-level navigator, provided you aren't passing your own navigation prop as you would with a redux integration. The presented approach is useful in situations when you want to trigger a navigation action from places where you do not have access to the navigation prop, or if you're looking for an alternative to using the navigation prop.

You can get access to a navigator through a ref and pass it to the NavigationService which we will later use to navigate. Use this only with the top-level (root) navigator of your app.

// App.js

import NavigationService from './NavigationService';

const TopLevelNavigator = createStackNavigator({ /* ... */ })

class App extends React.Component {
  // ...

  render() {
    return (
      <TopLevelNavigator
        ref={navigatorRef => {
          NavigationService.setTopLevelNavigator(navigatorRef);
        }}
      />
    );
  }
}

In the next step, we define NavigationService which is a simple module with functions that dispatch user-defined navigation actions.

 // NavigationService.js

import { NavigationActions } from 'react-navigation';

let _navigator;

function setTopLevelNavigator(navigatorRef) {
  _navigator = navigatorRef;
}

function navigate(routeName, params) {
  _navigator.dispatch(
    NavigationActions.navigate({
      routeName,
      params,
    })
  );
}

// add other navigation functions that you need and export them

export default {
  navigate,
  setTopLevelNavigator,
};

Then, in any of your javascript modules, just import the NavigationService and call functions which you exported from it. You may use this approach outside of your React components and, in fact, it works just as well when used from within them.

  // any js module
import NavigationService from 'path-to-NavigationService.js';

// ...

NavigationService.navigate('ChatScreen', { userName: 'Lucy' });

In NavigationService, you can create your own navigation actions, or compose multiple navigation actions into one, and then easily reuse them throughout your application. When writing tests, you may mock the navigation functions, and make assertions on whether the correct functions are called, with the correct parameters.

The best and only one working alternative for v3.*

@jdnichollsc
Copy link

@jdnichollsc jdnichollsc commented Feb 14, 2019

While the issue navigating back from nested navigators is fixed #5588, I added a workaround as you can see here https://github.com/proyecto26/react-native-modular/blob/master/src/services/Navigation/index.js#L218

It's required to use a "service" in order to register the history of the routes and compare after navigating to remove the initial route of the nested navigators :)
Any feedback is really appreciated 👍

@mixturify
Copy link

@mixturify mixturify commented Apr 7, 2019

2 or 3 days of pain with the navigation finding a solution for the undefined "this.props.navigation" !

When you use redux + nested navigation (switch navigation with 2 or more stack navigation) from single screen (login / register) to tab navigation... and you call the Main screen from App.js like this:

    <Provider store={store}>
      <View style={styles.container}>
        <MainScreen />
      </View>
  </Provider>

Not sure the base of the problem, but all of this stuff leads to the problem where I can't have access to store navigation inside MainScreen, but, I can call navigation inside the AuthLoadingScreen middleware (or TabNavigation...) for example.

So inside the MainScreen, I've putted the @microcipcip reducer solution - This will help me to reach the navigation where I can't (and trigger for example the logout function).

@iuliuvisovan
Copy link

@iuliuvisovan iuliuvisovan commented Nov 6, 2019

For those of you that just can't handle the bullshit anymore:

Main/Root/App.js:

<StackNavigator ref={(x) => (global.stackNavigator = x)} />

Anywhere:

global.stackNavigator.dispatch(
   NavigationActions.navigate({
       routeName: 'Player',
       params: { },
   }),
);
@oxbits
Copy link

@oxbits oxbits commented Apr 2, 2020

@iuliuvisovan

THANK YOU !!!!

i got your code to work pushing a button in a component in a stack navigator nested inside a switch navigator that makes the switch navigator change its current route.

however ....

if i put this:

global.stackNavigator.dispatch(
   NavigationActions.navigate({
       routeName: 'Player',
       params: { },
   }),
);

inside of a component's componentDidMount() function ( inside a double nest navigator ) i get this error:

Can't find variable: NavigationActions

<unknown>
    HomeScreenM.js:121:30
tryCallOne
    core.js:37:14
<unknown>
    core.js:123:25
_callTimer
    JSTimers.js:152:14
_callImmediatesPass
    JSTimers.js:200:17
callImmediates
    JSTimers.js:473:30
callImmediates
    [native code]:0
__callImmediates
    MessageQueue.js:337:6
<unknown>
    MessageQueue.js:135:6
__guard
    MessageQueue.js:314:10
flushedQueue
    MessageQueue.js:134:17
flushedQueue
    [native code]:0
invokeCallbackAndReturnFlushedQueue
    [native code]:0

any idea how i can run your code automatically upon instantiation of the app ?

@iuliuvisovan
Copy link

@iuliuvisovan iuliuvisovan commented Apr 2, 2020

Looks like an import error. Did you correctly import NavigationActions from 'react-navigation'?

@oxbits
Copy link

@oxbits oxbits commented Apr 3, 2020

@iuliuvisovan

silly me _ thanks !

got it working but also had to switch to using this instead :

global.switchNavigator

YOU ROCK !!!! 😺

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.