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

Dismiss the nested stack navigator


In my react-native project I am using libraries

"@react-navigation/native": "^5.8.10",
"@react-navigation/stack": "^5.12.8",

I have nested navigator, like this:

// root level I have a stack navigator where it contains two screens, `Home` and `Settings`.
const App = ()=> {
  const rootStack = createStackNavigator();
  return (
    <NavigationContainer>
      <rootStack.Navigator>
        <rootStack.Screen name="Home" component={Home} />
        <rootStack.Screen name="Settings" component={Settings} />
      </rootStack.Navigator>
    </NavigationContainer>
  );
}

// The Settings screen is a nested stack navigator
const Settings = ()=> {
  const settingsStack = createStackNavigator();
  return (
    <settingsStack.Navigator>
      <settingsStack.Screen name="SettingsOne" component={SettingsOneScreen} />
      <settingsStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
    </settingsStack.Navigator>
  );
}

As you can see, the Settings screen is actually another level (nested) stack navigator.

On SettingsOneScreen, there is a button navigates user to SettingsTwoScreen.

const SettingsOneScreen = ({navigation}) => {
   ...
   return (
      ...
      <Button onPress={()=>navigation.navigate("SettingsTwo")}/>
   )
}

Now, on SettingsTwoScreen, I have a button, I would like to close the whole settings navigator stack when user tap on the button. That's dismiss the whole settings stack & show user the Home. How to achieve it?

const SettingsTwoScreen = ({navigation}) => {
   ...
   return (
      ...
      <Button onPress={/*dismiss the settings stack*/}/>
   )
}

(Of course I can't use the navigation.goBack() which only navigate user back to the previous screen i.e. SettingOneScreen in this case.)


Solution

  • 1-) use navigate.

    //this will go back to Home and remove any screens after that. 
    navigation.navigate('Home')
    

    docs say that.

    In a stack navigator, calling navigate with a screen name will result in different behavior based on if the screen is already present or not. If the screen is already present in the stack's history, it'll go back to that screen and remove any screens after that. If the screen is not present, it'll push a new screen.

    2-) use reset.

    navigation.reset({
      index: 0,
      routes: [{ name: 'Home' }],
    })}
    

    see docs about reset

    3-) use replace then goBack.

    //from SettingsOne call replace instead navigate
    //this will remove SettingsOne and push SettingsTwo
    navigation.replace('SettingsTwo');
    
    
    //then from SettingsTwo 
    //calling goBack will back to home because SettingsOne removed in last step 
    navigation.goBack();
    

    4-) use one stack with pop.

    import { StackActions } from '@react-navigation/native';
    
    //merge two stacks in one
    <NavigationContainer>
       <rootStack.Navigator>
         <rootStack.Screen name="Home" component={Home} />
         <rootStack.Screen name="SettingsOne" component={SettingsOneScreen} />
         <rootStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
       </rootStack.Navigator>
    </NavigationContainer>
    
    //back 2 screen
    navigation.dispatch(StackActions.pop(2));
    

    see docs about pop

    for methods 1, 2 you can try snack here.