10
votes

I have a Tab Navigator inside a Stack Navigator and I want the header title to be dynamically configured as the title of the tab selected. Like there's 3 tabs: Home, Profile, Add Item and I want the header title to be "Home" when in the home tab, "Profile" when in the profile tab.

I tried using onStateChange on the root navigator and setOptions on the tab navigator but onStateChange is only available in the and not in the nested navigators.

Is their anyway I can archieve this?

The navigator config is:

const TabNav = (
   <Tab.Navigator>
      <Tab.Screen name='Home' component={HomeScreen}/>
      <Tab.Screen name='Profile' component={ProfileScreen}/>
      <Tab.Screen name='Add Item' component={AddItemScreen}/>
   </Tab.Navigator>
)

<NavigationContainer>
   <Stack.Navigator>
      <Stack.Screen name='Login' component={LoginScreen}/>
      <Stack.Screen name='App' component={TabNav}/>
   </Stack.Navigator>
</NavigationContainer>
2
Have you checked the official docs? reactnavigation.org/docs/en/… - satya164
@satya164 I tried both options in the docs but neither seem to work. The route prop doesn't have state key - Thiago Nascimento

2 Answers

7
votes

I had a similar Navigation hierarchy with react-navigation v5 and I wanted to change the Header Title inside a View in the nested TabNavigator. I finally achieved it by getting the parent navigation item of the StackNavigator with dangerouslyGetParent() and then using setOptions().

So here is your minimal Code for one of the three views inside your TabNav:

import React, {useCallback} from 'react';
import { useNavigation, useFocusEffect } from '@react-navigation/native';

const HomeScreen = (props) => {

    // TabNav navigation item
    const navigation = useNavigation();

    // Effect will be triggered everytime the Tab changes 
    //      Mounting is not enough -> Tabs will not be unmount by change
    useFocusEffect(
        useCallback(() => {

            // Get StackNav navigation item
            const stackNavigator = navigation.dangerouslyGetParent();
            if(stackNavigator){

                // Actually set Title
                stackNavigator.setOptions({
                    title: "Home"
                });
            }
        }, [navigation])
    );
    return (
        /* Component Items */
    );
};
1
votes

From documentation https://reactnavigation.org/docs/screen-options-resolution/

import { getFocusedRouteNameFromRoute } from '@react-navigation/native';

function getHeaderTitle(route) {
  // If the focused route is not found, we need to assume it's the initial screen
  // This can happen during if there hasn't been any navigation inside the screen
  // In our case, it's "Feed" as that's the first screen inside the navigator
  const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';

  switch (routeName) {
    case 'Feed':
      return 'News feed';
    case 'Profile':
      return 'My profile';
    case 'Account':
      return 'My account';
  }
}

<Stack.Screen
  name="Home"
  component={HomeTabs}
  options={({ route }) => ({
    headerTitle: getHeaderTitle(route),
  })}
/>