“This is my NTH day of participating in the First Challenge 2022. For details: First Challenge 2022.”

I seem to have made fun of RN many times, haha, but it is true that many project teams in China use RN. As we all know, RN’s official libraries are not enough, even not good enough, such as Navigation library. Currently, the recommended Navigation library is not RN’s official library, but the third-party React Navigation.

React Navigation? React Navigation provides a variety of Navigation modes, such as stack, TAB, and drawer.

But what if all this navigation doesn’t meet your needs and you need to customize your own navigation? If you want to build a wheel from 0, it’s too complicated. After all, the client is not the same as the Web side. At this time, we don’t want to start from 0 early wheels, or use parts to assemble it

React Navigation gives us perfect parts. How to use React Navigation to customize your Navigator?

Environment to prepare

Install the React Native CLI using the React Native CLI.

npx react-native init lesson3
cd lesson3
yarn ios
yarn android
Copy the code

In addition to the default package, we need to install @react-navigation/bottom-tabs, @react-navigation/native, react-native safe-area-context, etc. Let’s go directly to package.json.

{
  "name": "lesson3"."version": "0.0.1"."private": true."scripts": {
    "android": "react-native run-android"."ios": "react-native run-ios"."start": "react-native start"."test": "jest"."lint": "eslint ."
  },
  "dependencies": {
    "@react-navigation/bottom-tabs": "^ 6.0.9"."@react-navigation/native": "^ 6.0.6"."@react-navigation/native-stack": "^ 6.2.4." "."react": "17.0.2"."react-native": "0.66.0"."react-native-elements": "^ 3.4.2." "."react-native-safe-area-context": "^ 3.3.2 rainfall distribution on 10-12"."react-native-screens": "^ 3.8.0." "."react-native-vector-icons": "^ 8.1.0",},"devDependencies": {
    "@babel/core": "^ 7.12.9"."@babel/runtime": "^ 7.12.5"."@react-native-community/eslint-config": "^ 2.0.0." "."babel-jest": "^ 26.6.3"."eslint": "7.14.0"."jest": "^ 26.6.3"."metro-react-native-babel-preset": "^ 0.66.2"."react-test-renderer": "17.0.2"
  },
  "jest": {
    "preset": "react-native"}}Copy the code

After installing the React Navigation library, don’t forget to go to the ios file and install the ios-related dependencies:

cd ios && pod install
Copy the code

CreateMyNavigator creates a function that creates the following functions:

const {Navigator, Screen, Group} = createMyNavigator();
Copy the code

Let’s implement a Tab navigation step by step:

Code implementation and principle

createNavigatorFactory

It’s HOC. Folks, remember what HOC is? HOC, a Higher Order Component, is a function whose arguments and return values are components. This function is used to create the Navigator and Screen, so the next thing we need to implement is the TabNavigator component

const createMyNavigator = createNavigatorFactory(TabNavigator);

export default createMyNavigator;
Copy the code

TabNavigator

This is a component that controls how the navigation is displayed and how the page is displayed. that is, this component displays two parts of the content: the navigation and the page. The TabNavigator component shelf is:

function TabNavigator({ initialRouteName, children, screenOptions, tabBarStyle, contentStyle, }) {
  return (
    <NavigationContent>
        <View>
            <Text>navigation</Text>
        </View>
        <View>
            <Text>page</Text>
        </View>
    </NavigationContent>
  );
}
Copy the code

In addition, the TabNavigator receives props parameters, including children, screenOptions, and so on, as child nodes, Screen parameters, and so on, which are all children from the component Navigator:

const {Navigator, Screen, Group} = createMyTabNavigator();

export default function HomeRouterScreen() {
  return (
    <Navigator>
    
      <Screen name="home" component={HomeScreen} options={{title:}} />
      <Screen
        name="user"
        component={UserScreen}
        options={{title:'User center '}} />

      <Screen
        name="setting"
        component={SettingScreen}
        options={{title:'Setup '}} />

    </Navigator>
  );
}
Copy the code

Before we move on to navigation and page components, we need to look at one more custom Hook: useNavigationBuilder. It takes the children component and the page configuration information screenOptions as parameters. Returns information about the child components of the Navigator component or called child routes, such as an array of child routing information. UseNavigationBuilder uses the following:

function TabNavigator({children, screenOptions}) {
  const {state, descriptors, navigation, NavigationContent} =
    useNavigationBuilder(TabRouter, {
      children,
      screenOptions,
    });
     return (
    <NavigationContent>// omit ten thousand words</NavigationContent>
  );
}
Copy the code

Ok, now we can implement the navigation component and the page component parts separately.

Navigation components

It is said that navigation components, in fact, sometimes there is no component, but the navigation mode, such as stack navigation is only in the stack of data information changes, and there is no displayed components. But the Tab navigation we’re writing now has navigation components.

Ok, so let’s implement the Tab navigation component here, which is actually an array traversal process, traversing all the navigation configuration, and then adding the click event to switch the page:

     / / navigation
      <View style={[{flexDirection: 'row'.paddingTop: 50}, tabBarStyle]}>
        {state.routes.map((route, index) = > (
          <Pressable
            title={descriptors[route.key].options.title || route.name}
            key={route.key}
            onPress={()= >{ const event = navigation.emit({ type: 'tabPress', target: route.key, canPreventDefault: true, }); if (! event.defaultPrevented) { navigation.dispatch({ ... TabActions.jumpTo(route.name), target: state.key, }); }}} Style ={{flex: 1, justifyContent: 'space-between', alignItems: 'center',}}> {/* Display title or route.name */}<Text>{descriptors[route.key].options.title || route.name}</Text>
          </Pressable>
        ))}
      </View>
Copy the code

page

The page display, like the navigation component, iterates through all the page data, displays the page if it matches the current page, otherwise hides it, and displays the page through the implementation of descriptor.render(). The matching page can be judged by state.index, i.e. the page index. Every navigation switch will update state.index, so we can judge whether to display the page by state.index===index.

    / / page
    <View style={[{flex: 1}, contentStyle]}>
        {state.routes.map((route, index) = > {
          const descriptor = descriptors[route.key];
          const isFocused = state.index === index;
          return (
            <Screen
              key={route.key}
              focused={isFocused}
              route={descriptor.route}
              navigation={descriptor.navigation}
              header={<Header title={descriptor.options.title || route.name} / >}
              headerShown={descriptor.options.headerShown}
              style={[
                StyleSheet.absoluteFill,
                {display: index === state.index ? 'flex' : 'none'},
              ]}>
              {descriptor.render()}
              {/* <Text>{JSON.stringify(state)}</Text>* /}</Screen>
          );
        })}
      </View>
Copy the code

Above, custom navigation we will be completed, the code is very detailed, we can try ~

If you want the full runnable code, look here.