Sharing Data Across Navigators
When navigators are nested, you sometimes need to reach across boundaries — set the drawer title from a screen inside a tab, or navigate to a screen in a different stack. React Navigation provides tools for this, but there are pitfalls to watch for.
Accessing the Parent Navigator
Every screen gets a navigation prop scoped to its nearest navigator. To access a parent navigator, use navigation.getParent():
function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Button
title="Open Drawer"
onPress={() => navigation.getParent().openDrawer()}
/>
</View>
);
}
If HomeScreen is inside a Stack that's inside a Drawer, navigation.getParent() returns the drawer's navigation object.
Navigating Across Nested Navigators
You can navigate to a screen in a different navigator by specifying the target and passing a nested screen param:
navigation.navigate('Settings', {
screen: 'EditProfile',
params: { userId: 42 },
});
This tells React Navigation: switch to the "Settings" tab, then navigate to "EditProfile" within it, passing { userId: 42 } as params.
Setting Parent Navigator Options
A screen inside a nested stack can update the parent tab or drawer options:
function HomeDetailsScreen({ navigation }) {
React.useEffect(() => {
const parent = navigation.getParent();
parent?.setOptions({ tabBarStyle: { display: 'none' } });
return () => {
parent?.setOptions({ tabBarStyle: undefined });
};
}, [navigation]);
return (
<View style={styles.container}>
<Text style={styles.title}>Details (tab bar hidden)</Text>
</View>
);
}
This hides the tab bar when the Details screen is focused and restores it when leaving. The cleanup in the return function is important — without it the tab bar stays hidden after navigating back.
Using React Context for Shared State
Route params work for simple data, but for state that many screens need (like the current user or theme), React Context is cleaner:
import React from 'react';
const UserContext = React.createContext(null);
function App() {
const [user, setUser] = React.useState({ name: 'Jason', id: 1 });
return (
<UserContext.Provider value={user}>
<NavigationContainer>
{/* navigators */}
</NavigationContainer>
</UserContext.Provider>
);
}
function ProfileScreen() {
const user = React.useContext(UserContext);
return (
<View style={styles.container}>
<Text style={styles.title}>Hello, {user.name}</Text>
</View>
);
}
Context lives outside the navigation tree, so any screen in any navigator can access it without threading params through navigate calls.
Common Pitfalls
Screen name collisions. If two navigators have screens with the same name, navigate finds the closest one. Be explicit — use unique names or the nested screen param syntax.
Resetting nested state. Navigating to a tab doesn't reset its stack. If you want to go to a tab's root screen, pass screen explicitly:
navigation.navigate('Home', { screen: 'HomeMain' });
Stale closures. When using navigation.getParent() in effects, include navigation in the dependency array to avoid stale references.
In the next lesson we'll apply what we've learned to a real-world pattern: authentication flows.