Ian Vasco
Ian Vasco

Reputation: 1340

React Navigation v5 setOptions not working

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!

Upvotes: 3

Views: 5041

Answers (3)

artsnr
artsnr

Reputation: 1032

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>
      ),
    });
  }
}, []);

Upvotes: 5

Zahra Mirali
Zahra Mirali

Reputation: 545

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]);
}

Upvotes: 0

Sameer Kumar Jain
Sameer Kumar Jain

Reputation: 2135

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

Upvotes: 3

Related Questions