在反应导航中如何在不同的嵌套堆栈之间导航

目标

使用反应导航,从导航器中的屏幕导航到另一个导航器中的屏幕。

更多细节

如果我有以下导航器结构:

  • 家长导航员
    • 嵌套导航器1
      • 屏幕 A
      • 屏幕 B
    • 嵌套导航2
      • 屏幕 C
      • 屏幕 D

如何从嵌套导航器2下的屏幕 D 转到嵌套导航器1下的屏幕 A?现在,如果我尝试 navigation.navigate屏幕 A 从屏幕 D 将有一个错误,说它不知道屏幕 A,只有屏幕 C 和 D。

我知道这个问题已经在这个网站和 GitHub (https://github.com/react-navigation/react-navigation/issues/983https://github.com/react-navigation/react-navigation/issues/335#issuecomment-280686611)上以不同的形式被问过了,但是对于这么基本的东西,缺乏清晰的答案,在 GitHub 上滚动数百条评论寻找解决方案并不是很好。

也许这个问题可以为每个遇到这个非常普遍的问题的人编写一个解决方案。

131480 次浏览

Update: For React Navigation v5, see @mahi-man's answer.


You can use the third parameter of navigate to specify sub actions.

For example, if you want to go from screen D under nested navigator 2, to screen A under nested navigator 1:

this.props.navigation.navigate(
'NestedNavigator1',
{},
NavigationActions.navigate({
routeName: 'screenB'
})
)

Check also: https://reactnavigation.org/docs/nesting-navigators/

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.

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.

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'))
}

React Navigation v3:

Navigation.navigate now takes one object as the parameter. You set the stack name then navigate to the route within that stack as follows...

navigation.navigate(NavigationActions.navigate({
routeName: 'YOUR_STACK',
action: NavigationActions.navigate({ routeName: 'YOUR_STACK-subRoute' })
}))

Where 'YOUR_STACK' is whatever your stack is called when you create it...

  YOUR_STACK: createStackNavigator({ subRoute: ... })
 const subAction = NavigationActions.navigate({ routeName: 'SignInScreen' });
AsyncStorage.clear().then(() =>
this.props.navigation.navigate('LoggedOut', {}, subAction));

LoggedOut is the stack name where signIn screen is placed.

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.

In React Navigation 3

@ZenVentzi, Here is the answer for multi-level nested navigators when Nested Navigator 1 has Nested Navigator 1.1.

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

We can just inject NavigationActions.navigate() several times as needed.

  const subNavigateAction = NavigationActions.navigate({
routeName: 'NestedNavigator1.1',
action: NavigationActions.navigate({
routeName: 'ScreenB',
params: {},
}),
});
const navigateAction = NavigationActions.navigate({
routeName: 'NestedNavigator1',
action: subNavigateAction,
});
this.props.navigation.dispatch(navigateAction);

UPDATE For React Navigation 5, please check @mahi-man's answer above. https://stackoverflow.com/a/60556168/10898950

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: { },
}),
);

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

In React Navigation 5, this becomes much easier by passing in the screen as the second parameter:

navigation.navigate('Nested Navigator 2', { screen: 'screen D' });

You can also include additional levels if you have deeply nested screens:

navigation.navigate('Nested Navigator 2', {
screen: 'Nested Navigator 3', params: {
screen: 'screen E'
}
});

On React Navigation v5 you have here all the explanation:

https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator

Route definition

function Root() {
return (
<Stack.Navigator>
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
);
}


function App() {
return (
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Root" component={Root} />
</Drawer.Navigator>
</NavigationContainer>
);
}

Instruction

navigation.navigate('Root', { screen: 'Settings' });

In React Navigation V5, you can do like this:

but remember to you placing this on the parent side

this.props.navigation.navigate(
'Nested Navigator 1',
{name: 'jane'},
this.props.navigation.navigate('Screen A', {id: 2219}),
);

In React Navigation v5, you can do something like:

navigation.navigate('Root', {
screen: 'Settings',
params: {
screen: 'Sound',
params: {
screen: 'Media',
},
},
});

In the above case, you're navigating to the Media screen, which is in a navigator nested inside the Sound screen, which is in a navigator nested inside the Settings screen.

In React Navigation v5/v6

Navigation to Specific Screen on a Stack

navigation.navigate('Home', {
screen: 'Profile',
params: {userID: 1}
}
)

What If We Nest More?

Consider this structure:

NAVIGATOR:
*StackA
*ScreenC
*ScreenD
*StackB
*ScreenI
*StackE
*ScreenF
*StackG
*ScreenJ
*ScreenH

We want to get from ScreenC inside StackA all the way to ScreenH in StackB. We can actually chain the parameters together to access specific screens.

navigation.navigate('StackB',{
screen: 'StackE',
params: {
screen: 'StackG',
params: {
screen: 'ScreenH'
}
}
}
)

For more information

This is another way to navigate to nested screen using Version: 5.x. It worked without any additional configuration. More info here: https://reactnavigation.org/docs/use-link-to

const linkTo = useLinkTo();
// ...
// Just call this
linkTo(`/main/sub/subB`);

React Navigation v6


docs

function Home() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={Feed} />
<Tab.Screen name="Messages" component={Messages} />
</Tab.Navigator>
);
}


function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options=\{\{ headerShown: false }}
/>
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
</NavigationContainer>
);
}

navigation.navigate('Home', { screen: 'Messages' });