3

I'm trying to set the navigation options from my stack screen inside my component since I need to render a right header button with specific actions from my screen. Problem is that it is not working. The structure I'm using is as follows:

-StackNavigator
  -MaterialTopTabNavigator
    -tab1
    -tab2
    -tab3

Current code:

//My navigation
    <NavigationContainer initialState={initialState} ref={ref}>
      <Stack.Navigator screenOptions={{ headerShown: false }}>
        //some other Stacks
        <Stack.Screen
          name="Detail"
          component={DetailTabScreen}
          options={{
            headerShown: true, //header works ok
            headerRight: () => ( //this would work
              <View style={{backgroundColor: 'red', width: 100}}/>
            )
          }}
        />
      </Stack.Navigator>
    </NavigationContainer>

My DetailTabScreen:

const TopTab = createMaterialTopTabNavigator()
const DetailTabScreen = () => (
  <StoreProvider>
    <SafeAreaView style={{ flex: 1 }}>
      <TopTab.Navigator
        tabBarOptions={{
         ...options
        }}
      >
        <TopTab.Screen
          name="PlanDetail"
          component={PlanDetail}
          options={{
            tabBarLabel: ({ color }: TabBarLabelProps) => (
              <Text>Detalles</Text>
            ),
          }}
        />
        <TopTab.Screen
          name="PlanChat"
          component={PlanChat}
          options={{
            tabBarLabel: ({ color }: TabBarLabelProps) => (
              <Text>Chat</Text>
            ),
          }}
        />
        <TopTab.Screen
          name="Participants"
          component={Participants}
          options={{
            tabBarLabel: ({ color }: TabBarLabelProps) => (
              <Text>Participantes</Text>
            ),
          }}
        />
      </TopTab.Navigator>
    </SafeAreaView>
  </StoreProvider>
)

I already have tried setting the options inside the component as the docs suggests using the useLayoutEffect, also tried using the useEffect:

  useLayoutEffect(() => {
    props.navigation.setOptions({
      headerRight: () => (//does not work
        <TouchableOpacity
          onPress={() => console.warn('This is a button!')}
          style={{marginRight:16}}
        >
          <Icon name={Platform.OS === 'ios' ? 'share-apple' : 'share-google'} type="EvilIcons" style={{ color: colors.darkGreen, marginright: 16 }} />
        </TouchableOpacity>
      ),
    });
  }, []);

PS: I know that hiding the header and using a custom component for my header would work as a work around but I want to know why navigation.setOptions is not working.

Any help, any ideas would be appreciated, thanks!

Ian Vasco
  • 1,280
  • 13
  • 17

3 Answers3

4

This helped me out. I solved it by accessing parent stack navigator via dangerouslyGetParent() method and setting its options.

useLayoutEffect(() => {

  const stackNavigator = props.navigation.dangerouslyGetParent(); // this is what you need

  if (stackNavigator) {
    stackNavigator.setOptions({
      headerRight: () => (
        <TouchableOpacity
          onPress={() => console.warn("This is a button!")}
          style={{ marginRight: 16 }}
        >
          <Icon
            name={Platform.OS === "ios" ? "share-apple" : "share-google"}
            type="EvilIcons"
            style={{ color: colors.darkGreen, marginright: 16 }}
          />
        </TouchableOpacity>
      ),
    });
  }
}, []);
artsnr
  • 952
  • 1
  • 10
  • 27
  • 3
    You're the man now, dog. FYI, in `react-navigation` v5+, it is just called `getParent()`. I guess that so many people needed to use it, they just dropped the "dangerously" prefix. – Joshua Pinter Oct 03 '21 at 18:15
3

A tab navigator itself doesn't have any header. Children's inside TopTab.Navigator won't have access to parent navigation ie Stack.Navigator. You either need to pass an instance of parent navigation to the children of tabs or hide the header from the stack and create your own header component inside tab children. Another option is to attach a listener to tab screen like below and then you can update the header of the parent stack.

const DetailTabScreen = (props) => {
....

<TopTab.Screen
    name="PlanDetail"
    component={PlanDetail}
    options={{
        tabBarLabel: ({ color }: TabBarLabelProps) => (
            <Text>Detalles</Text>
        ),
    }}
    listeners={{
        tabPress: (e) => {
            // Prevent default action
            props.navigation.setOptions({
                title: 'PlanDetail',
            });
        },
    }}
/>

You can find more details here

Sameer Kumar Jain
  • 2,074
  • 1
  • 13
  • 22
0
function SomeScreen({navigation}) {
  useLayoutEffect(() => {
    navigation.setOptions({
      headerRight: () => <TouchableOpacity
        onPress={() => console.warn('This is a button!')}
        style={{ marginRight: 16 }}>
        <Icon name={Platform.OS === 'ios' ? 'share-apple' : 'share-google'}
              type="EvilIcons" style={{ color: colors.darkGreen, marginright: 16 }} />
      </TouchableOpacity>
    });
  }, [navigation]);
}
Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
Zahra Mirali
  • 535
  • 3
  • 10