Search code examples
react-nativereact-navigation-stackreact-navigation-v6

How show back button in screen component in react navigaton


I use my own header in React Navigation and according to the documentation there is such a description:

Custom header to use instead of the default header.

This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument:

navigation - The navigation object for the current screen.
route - The route object for the current screen.
options - The options for the current screen
back - Options for the back button, contains an object with a title property to use for back button label.
Example:

import { getHeaderTitle } from '@react-navigation/elements';

// ..

header: ({ navigation, route, options, back }) => {
  const title = getHeaderTitle(options, route.name);

  return (
    <MyHeader
      title={title}
      leftButton={
        back ? <MyBackButton onPress={navigation.goBack} /> : undefined
      }
      style={options.headerStyle}
    />
  );
};

To set a custom header for all the screens in the navigator, you can specify this option in the screenOptions prop of the navigator.

Note that if you specify a custom header, the native functionality such as large title, search bar etc. won't work.

The header property accepts the back parameter which can be undefined or an object with the title property and used this code and now wants to determine whether to show me a Back Button or not, just like in the code example, but I don't I know how to cause the appearance of this button from the screen or through a property in the Stack.Screen component, the only thing I came up with is to rewrite the header like this:

const SomeScreen = () => {
  const { navigation } = useNavigator()

  useEffect(() => {
    navigation
      .setOptions({ header: () =>  (
        <MyHeader
          title={title}
          leftButton={<MyBackButton onPress={navigation.goBack} />}
          style={options.headerStyle}
        />
      )});
  }, [])

  return (
    <Layout>
      <View style={[screenStyles.screenTopPadding, screenStyles.screenPadding]}>
      </View>
    </Layout>
  )
}

How can I tell the back parameter to perform a ternary operation?


Solution

  • I did not find a way to provoke the change of back from undefine to an object, but I found a more correct solution in my opinion. It consists in using the headerBackVisible property with options that are passed to the same header method:

    header: ({ navigation, route, options: { headerBackVisible }, back }) => {
      ...
    
      return (
        <MyHeader
          title={title}
          leftButton={
            headerBackVisible ? <MyBackButton onPress={navigation.goBack} /> : undefined
          }
          style={options.headerStyle}
        />
      );
    };
    

    After that, I wrote a hook that should be used in the Screen on which this change should be turned on/off:

    import { useEffect } from 'react'
    import { useNavigation } from '@react-navigation/native'
    import { Routes } from '@/config/routes'
    import { ScreenNavigation } from '@/navigation/types'
    import { useIsFocused } from '@react-navigation/native'
    
    const useBackArrow = (isShow: boolean, parents: Routes[]): void => {
      const navigation = useNavigation()
      const isFocused = useIsFocused()
    
      useEffect(() => {
        let parent = navigation
        while (parents.length !== 0) {
          const route = parents.shift() as Routes
    
          parent = parent.getParent<ScreenNavigation<typeof route>>()
        }
    
        if (!isFocused) {
          return
        }
    
        parent.setOptions({ headerBackVisible: isShow })
      }, [isShow, parents, navigation, isFocused])
    }
    
    export default useBackArrow
    

    And I use it as follows:

    const SomeScreen = () => {
      useBackArrow(true, [/** parents navigator if is no root */])
    
      return (
        <Layout>
          <View>
            Some screen
          </View>
        </Layout>
      )
    }
    
    export default SomeScreen
    

    The disadvantages of such an approach are that you need to add this hook in a circle so that the state of the arrow corresponds to what you want, maybe there is a better solution to this problem in the community, if so I will wait for your answer.