React Navigation v5: Reset Stack Inside Tab After Leaving Tab

Author

Spencer Carli

Developer, cat dad, and devout pizza lover. Teaching at React Native School and building apps with Handlebar Labs.

Last Updated: February 10, 2021

Problem: You have a stack navigator inside a tab and, when going to a different tab, you want to reset the stack navigator inside the tab you just left.

Example

import * as React from 'react';
import { View, Text, Button } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { CommonActions, StackActions } from '@react-navigation/native';

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
      <Button title="Details" onPress={() => navigation.push('Details')} />
    </View>
  );
}

function DetailsScreen() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Details Screen</Text>
    </View>
  );
}

function SettingsScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Settings Screen</Text>
      <Button
        title="Settings Details"
        onPress={() => navigation.push('SettingsDetail')}
      />
    </View>
  );
}

function SettingsDetailScreen() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Settings Details Screen</Text>
    </View>
  );
}

const Stack = createStackNavigator();
const Stack2 = createStackNavigator();
const Tab = createBottomTabNavigator();

const HomeStack = () => (
  <Stack.Navigator>
    <Stack.Screen name="Home" component={HomeScreen} />
    <Stack.Screen name="Details" component={DetailsScreen} />
  </Stack.Navigator>
);

const SettingsStack = () => (
  <Stack2.Navigator>
    <Stack2.Screen name="Settings" component={SettingsScreen} />
    <Stack2.Screen name="SettingsDetail" component={SettingsDetailScreen} />
  </Stack2.Navigator>
);

function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="HomeTab" component={HomeStack} />
        <Tab.Screen name="SettingsTab" component={SettingsStack} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

export default App;

For this example app I only want to reset the home stack if I leave that tab. If I'm in a nested screen on settings I want to leave it as is.

https://media.giphy.com/media/FcMdPWSY547cAPhStD/giphy.gif

Solution

This isn't a great solution (notice the "dangerouslyGetState") but it seems to work well enough. This solution is built on top of the solution provided in this Github issue.

// ...

// https://github.com/react-navigation/react-navigation/issues/8583
const TAB_TO_RESET = 'HomeTab';
const resetHomeStackOnTabPress = ({ navigation, route }) => ({
  tabPress: (e) => {
    const state = navigation.dangerouslyGetState();

    if (state) {
      // Grab all the tabs that are NOT the one we just pressed
      const nonTargetTabs = state.routes.filter((r) => r.key !== e.target);

      nonTargetTabs.forEach((tab) => {
        // Find the tab we want to reset and grab the key of the nested stack
        const tabName = tab?.name;
        const stackKey = tab?.state?.key;

        if (stackKey && tabName === TAB_TO_RESET) {
          // Pass the stack key that we want to reset and use popToTop to reset it
          navigation.dispatch({
            ...StackActions.popToTop(),
            target: stackKey,
          });
        }
      });
    }
  },
});

function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen
          name="HomeTab"
          component={HomeStack}
          // Note that resetHomeStackOnTabPress should be added to each tab
          listeners={resetHomeStackOnTabPress}
        />
        <Tab.Screen
          name="SettingsTab"
          component={SettingsStack}
          listeners={resetHomeStackOnTabPress}
        />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

export default App;
https://media.giphy.com/media/S4A8Vj7sjIvazucZ0r/giphy.gif

If you want every stack to reset when changing tabs you can remove the tabName === TAB_TO_RESET check.

React Native School Logo

React Native School

Want to further level up as a React Native developer? Join React Native School! You'll get access to all of our classes and our private Slack community.

Join the email list to be notified of all new lessons and classes!