0

React Navigation: Component in Tab Navigator not unmounting properly after parent Stack Navigator reset

The Problem I have a nested navigation structure with a Stack Navigator containing a Tab Navigator. When resetting the Stack Navigator to the initial route, components within the Tab Navigator are not unmounting properly, causing state-related issues.

// Navigation Structure
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();

function TabNavigator() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
      {/* other tabs */}
    </Tab.Navigator>
  );
}

function RootNavigator() {
  return (
    <Stack.Navigator>
      <Stack.Screen name="Login" component={LoginScreen} />
      <Stack.Screen name="MainApp" component={TabNavigator} />
      {/* other screens */}
    </Stack.Navigator>
  );
}

When logging out from the Profile screen, I reset the navigation:

function ProfileScreen() {
  const navigation = useNavigation();
  
  const handleLogout = async () => {
    // Reset to Login screen
    navigation.reset({
      index: 0,
      routes: [{ name: 'Login' }]
    });
    
    // Clear app state
    clearAppState();
  };
  
  return (/* ... */);
}

The Issue Even though the navigation resets successfully, components within the Tab Navigator are not unmounting immediately. This causes issues because these components are still mounted when the app state is cleared, leading to errors as they try to access now-null values. I've verified this by adding unmount logs in the components:

useEffect(() => {
  console.log('Component mounted');
  return () => {
    console.log('Component unmounting');  // This log appears too late
  };
}, []);

Adding a delay before clearing the app state works, but feels hacky:

const handleLogout = async () => {
  navigation.reset({
    index: 0,
    routes: [{ name: 'Login' }]
  });
  
  await new Promise(resolve => setTimeout(resolve, 500));  // Why needed?
  clearAppState();
};

Questions

  1. Why aren't the Tab Navigator components unmounting immediately after the navigation reset?

  2. Is there a way to properly wait for all components to unmount before clearing app state?

  3. Is there a better pattern for handling logout in nested navigator scenarios?

Environment

  • react-navigation: ^6.x
  • @react-navigation/native: ^6.x
  • @react-navigation/stack: ^6.x
  • @react-navigation/bottom-tabs: ^6.x
  • react-native: 0.7x.x

I've tried:

  1. Using unmountOnBlur: true in the Tab Navigator options (this works but I rather not unmount the component on every blur event but only on logout)
  2. Using the navigation reference directly
  3. Adding navigation state change listeners
  4. Getting the root navigator via navigation.getParent()

None of these approaches ensure the components are unmounted before the app state is cleared.

7
  • Login and MainApp should conditionally render based on a logged in value instead of having them both mounted. Only one should be rendered at a time. reactnavigation.org/docs/6.x/auth-flow Commented Dec 28, 2024 at 22:37
  • @johnhaup The thing is that I have some screens that are accessible to the user as guest, that is without signing in, that's why you can navigate to both login and home. Commented Dec 29, 2024 at 1:16
  • Those screens can live in a separate shared stack that is always mounted, or they can be added to both stacks. You can also get really granular with the behavior of the shared screens as described here, if necessary: reactnavigation.org/docs/6.x/auth-flow/… Commented Dec 29, 2024 at 17:56
  • Thanks for the advice, I might refactor that part of the application, but it won't fix my issue here, right? the logged in stack will still behave like I described above. That is, components subscribed to the state will still react to the state change after I reset the stack. It seems to run some event loop cycles before the components are unmounted after I reset the stack. Do you know if there is any way to know when all component have unmounted so that I can clear the state after that? or a better way to implement this so that it wont be an issue? (I'm more of a web dev than mobile dev). Commented Dec 29, 2024 at 20:07
  • The issue might be in that I'm using patterns that I know from web development that might not translate directly to mobile apps. I'm using zustand to implement redux for the state management, and the above described issue is not a problem in web because components unmount when navigating away from them which is different in mobile, which may mean I need to think differently about state management. If you have any pointers on how better manage local state in mobile would be greatly appreciated (maybe the pattern I know from web is the problem). Commented Dec 29, 2024 at 20:18

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.