Search code examples
react-nativereact-navigationreact-navigation-stack

React Navigation - navigation.navigate() not navigating using top window


I use the following structure in my React Native project.

<NavigationContainer>
  <Stack.Navigator>
    <Stack.Screen name="Login" component={LoginScreen} />
    <Stack.Screen
      name="Home"
      component={HomeScreen}
      options={{
        headerRight: () => <SettingsButton />,
        presentation: 'modal',
      }}
    />
    <Stack.Screen
      name="Settings"
      component={SettingsScreen}
      options={{
        presentation: 'card',
      }}
    />
  </Stack.Navigator>
</NavigationContainer>

function SettingsButton() {
  const navigation = useNavigation();

  return (
    <Button 
      title="Settings" 
      onPress={() => navigation.navigate('Settings')} 
    />
  );
}

Whenever SettingsButton is pressed, I want to open my Settings window on the home screen (foreground window) and not the login screen (background/root window). Unfortunately, React Navigation uses the root window to push the screen and this happens (notice how the back button appears on the background window):

screenshot of the two windows

How can I make it so that it uses the foreground window?


Solution

  • Note: There is a separate issue here which may cause you trouble down the road. I've added a description below.

    Regarding the question at hand, how about passing a stack to the modal?

    export const Root = () => {
        const RootStack = useMemo(() => createStackNavigator(), []);
        const HomeStack = useMemo(() => createStackNavigator(), []);
    
        function renderHomeStack() {
            return (
                <HomeStack.Navigator>
                    <HomeStack.Screen name="Home" component={HomeScreen} />
                    <HomeStack.Screen name="Settings" component={SettingsScreen} />
                </HomeStack.Navigator>
            );
        }
    
        function renderRootStack() {
            return (
                <RootStack.Navigator>
                    <RootStack.Screen name="Login" component={LoginScreen} />
                    <RootStack.Screen
                        name="HomeStack"
                        options={{
                            presentation: 'modal',
                        }}
                        component={renderHomeStack}
                    />
                </RootStack.Navigator>
            );
        }
    
        return renderRootStack();
    };
    

    You might note that react-navigation encourages the separation of the login stack and the main stack. You can read more about it here. If you don't, you might encounter multiple issues down the road such as:

    1. Blocking the back button in android to avoid going back to the login page after a successful login.
    2. Resetting navigation state to navigate more easily to inner pages with navigate:
      navigation.navigate('Root', { screen: 'Profile' });
      
    3. Implementing deep linking down the road to navigate based on a push notification.

    The bottom line is that implementing navigation is considerably easier when the stacks are separated.