How to navigate between different nested stacks in

2020-02-02 10:44发布

The Goal

Using react navigation, navigate from a screen in a navigator to a screen in a different navigator.

More Detail

If I have the following Navigator structure:

  • Parent Navigator
    • Nested Navigator 1
      • screen A
      • screen B
    • Nested Navigator 2
      • screen C
      • screen D

how do I go from screen D under nested navigator 2, to screen A under nested navigator 1? Right now if I try to navigation.navigate to screen A from screen D there will be an error that says it doesn't know about a screen A, only screen C and D.

I know this has been asked in various forms in various places on this site as well as GitHub(https://github.com/react-navigation/react-navigation/issues/983, https://github.com/react-navigation/react-navigation/issues/335#issuecomment-280686611) but for something so basic, there is a lack of clear answers and scrolling through hundreds of GitHub comments searching for a solution isn't great.

Maybe this question can codify how to do this for everyone who's hitting this very common problem.

10条回答
叛逆
2楼-- · 2020-02-02 10:55

Complete freedom: singleton w/ navigationOptions

If you have a situation where you have multiple navigation stacks and sub stacks, this can be frustrating to know how to get a reference to the desired stack given how React Navigation is setup. If you were simply able to reference any particular stack at any given time, this would be much easier. Here's how.

  1. Create a singleton that is specific to the stack you want to reference anywhere.

    // drawerNavigator.js . (or stackWhatever.js)
    const nav = {}
    export default {
      setRef: ref => nav.ref = ref,
      getRef: () => nav.ref
    }
    
  2. Set the reference on desired navigator using navigatorOptions

    import { createBottomTabNavigator } from 'react-navigation'
    import drawerNavigation from '../drawerNavigator'
    
    const TabNavigation = createBottomTabNavigator(
      {
        // screens listed here
      },
      {
        navigationOptions: ({ navigation }) => {
          // !!! secret sauce set a reference to parent
          drawerNavigation.setRef(navigation)
          return {
            // put navigation options
          }
        }
      }
    )
    
  3. Now you can reference drawerNavigator anywhere inside or outside

    // screen.js
    import drawerNavigator from '../drawerNavigator'
    
    export default class Screen extends React.Component {
      render() {
        return (
          <View>
            <TouchableHighlight onPress={() => drawerNavigator.getRef().openDrawer()}>
              <Text>Open Drawer</Text>
            </TouchableHighlight>
          </View>
        )
      }
    }
    

Explanation

Within Step 2, a Tab Navigator is one of the screens within a Drawer Navigator. Tab Navigator needs to close the drawer but also anywhere within your app, you can call drawerNavigator.getRef().closeDrawer() after this step is performed. You are not limited to having direct access to props.navigation after that step.

查看更多
Bombasti
3楼-- · 2020-02-02 10:59

My goal was to have the authentication screens all share the same background and the rest of the app using the regular stack transition.

After hours I've found the solution is to have the createStackNavigator() in the same file as your component wrapper. So that you can successfully expose the static router as the document stated. This will avoid the You should only render one navigator explicitly in your app warning and you can use this.props.navigation.navigate('AnyScreen') to navigate to any nested screen.

AuthRouter.js

export const AuthNavigationStack = createStackNavigator(
  {
    Login: {
      screen: Login
    },
    CreateAccount: {
      screen: CreateAccount
    }
  }
);

export default class AuthContainer extends React.Component {
  constructor( props ) {
    super( props );
  }

  static router = AuthNavigationStack.router;

  render() {
    return (
      <ImageBackground
        style={ {
          width: '100%',
          height: '100%'
        } }
        source={ require( '../Images/johannes-andersson-yosimite.jpg' ) }
        blurRadius={ 10 }
      >
        <StatusBar
          barStyle="dark-content"
          backgroundColor="transparent"
          translucent={ true }
        />
        <AuthNavigationStack navigation={ this.props.navigation } />
      </ImageBackground>
    );
  }
}

MessengerRouter.js

export const MessengerStackNavigator = createStackNavigator(
  {
    Chat: {
      screen: Chat,
    },
    User: {
      screen: User,
    },
  }
);

export default class MainContainer extends React.Component {
  constructor( props ) {
    super( props );
  }

  static router = MessengerStackNavigator.router;

  render() {
    return <MessengerStackNavigator navigation={ this.props.navigation } />;
  }
}

Router.js

import { createStackNavigator } from 'react-navigation';

import AuthRouter from './AuthRouter';
import MessengerRouter from './MessengerRouter';

export const RootNavigationStack = createStackNavigator( {
  AuthContainer: {
    screen: AuthRouter,
    navigationOptions: () => ( {
      header: null
    } )
  },
  MessengerRouter: {
    screen: MessengerRouter,
    navigationOptions: () => ( {
      header: null
    } )
  }
} );

RootContainer.js

import { RootNavigationStack } from '../Config/Router';

class RootContainer extends Component {

  render() {
    return <RootNavigationStack />;
  }
}

Notes:

  • Pass header: null from the RootNaviagtionStack to the nested stacks to remove the overlapping header

  • If you navigate from Nested A to Nested B and use the back button, it will return you to the first screen in Nested B. Not a big problem but I haven't figured out how to fix it.

查看更多
萌系小妹纸
4楼-- · 2020-02-02 11:03

If nothing else works (as in my case), just do:

Main/Root/App.js:

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

Anywhere:

global.stackNavigator.dispatch(
   NavigationActions.navigate({
       routeName: 'Player',
       params: { },
   }),
);
查看更多
乱世女痞
5楼-- · 2020-02-02 11:09

try this,

Parent Navigator
  Nested Navigator 1
    screen A
    screen B
  Nested Navigator 2
    screen A
    screen C
    screen D

and then, there is no need to go A in 1 from D in 2, you can just go A from D both in 2, you can check here image or A stack navigator for each tab

查看更多
你好瞎i
6楼-- · 2020-02-02 11:09

While working on a react-native project, i came across same situation. I have tried multiple ways in navigating to screen but failed.

After many trials, I tried passing parents navigation object to children and made a navigation function call and it worked.

Now coming to your issues, If you want to navigation from screen D to screen A do follow these steps.

-> Pass nested navigator 2 navigation props to its children using screenProps.

export default class Home extends Component {
    static navigationOptions = {
        header:null
    };

    constructor(props) {
        super(props);
        this.state = {
            profileData: this.props.navigation.state.params,
            route_index: '',
        }
    }

    render() {
        return (
            <ParentNavigator screenProps={this.props.navigation} />
        );
    }
}

export const ParentNavigator = StackNavigator({
  // ScreenName : { screen : importedClassname }
  Nested1: { screen: nested1 },
  Nested2: { screen : nestes1 }
});

export const nested1 = StackNavigator({
  ScreenA: { screen: screenA },
  ScreenB: { screen : screenB }
});

export const nested2 = StackNavigator({
  ScreenC: { screen: screenC },
  ScreenD: { screen : screenD }
});

You can receive the navigation in children using

const {navigate} = this.props.screenProps.navigation;

Now this navigate() can be used to navigate between children.

I accept that this process is little confusing but i couldn't find any solutions so had to go with this as i have to complete my requirement.

查看更多
Lonely孤独者°
7楼-- · 2020-02-02 11:10

I've found also such solution here:

onPress={() =>
    Promise.all([
    navigation.dispatch(
        NavigationActions.reset({
            index: 0,
            // TabNav is a TabNavigator nested in a StackNavigator
            actions: [NavigationActions.navigate({ routeName: 'TabNav' })]
        })
    )
    ]).then(() => navigation.navigate('specificScreen'))
}
查看更多
登录 后发表回答