This article describes some of my practical experience in using Navigator Navigator when developing RN cross-platform applications, in case I forget, but also for others’ reference.

One, foreword

If you are new to reactNative and want to develop cross-platform, you can use React Navigation directly. If you’re only developing for iOS and want to keep the look and feel of iOS native, you can use NavigatorIOS components.

Navigator is the official navigation component, compatible with both iOS and Android. Starting with version 0.44, Navigator was stripped from the React Native core component library into a separate module called React-Native deprecated- Custom-Components. If you want to use Navigator after 0.44, install react-native deprecated- custom-Components into the project and import it where necessary.

In the actual development process, Navigator performance is relatively good, also very stable, after all experienced so many versions of the test. UI performance is also good, almost as good as native. The removal of RN core libraries does not affect usage.

At the time of writing this article, our team was using version 0.44.3 of RN, which has officially been updated to version 0.51.

Why don’t I use React Navigation

We also considered using React Navigation directly, but we added RN function on the basis of existing Native engineering, and divided it into different modules according to different business functions. It is necessary to jump to different RN pages according to different values of Native in the same Module, that is to say, the initialRoute(root view) of the navigator is changed, not fixed. However, after studying React Navigation, it is suitable for the situation where the root view is fixed, so we have to abandon it.

Three, installation & use

1. Installation:

Execute the command in the project root directory (the same as node_modules) :

npm install react-native-deprecated-custom-components
Copy the code

Note: after my experience, it is best to execute the command NPM install before installation. Broken_heart: (broken_heart:)

Installation location: in ~/node_modules/react-native-deprecated-custom-components

Directory screenshot:

2. Usage: Navigator is used similarly to UINavigationController in iOS, and is generally used as the root view. Place all the subview components in the Navigator and return the Navigator in the corresponding Render function.

The code is as follows:

render() {
        return( <Navigator initialRoute={{title: this._getRootConmponet().title, id: this.state.rootPageKey, component: this._getRootConmponet().component}} configureScene={(route) => { return Navigator.SceneConfigs.PushFromRight; }} renderScene={(route, navigator) => { let Component = route.component; return <Component {... route.params} route={route} navigator={navigator} /> }} sceneStyle={{paddingTop: paddingTopOffset, paddingBottom:paddingBottomOffset}} navigationBar={ <Navigator.NavigationBar style={{ alignItems: 'center', backgroundColor: '#f8f8f8', borderBottomWidth:1/PixelRatio.get(), borderBottomColor:'#cccccc', }} routeMapper={RouteMapper} navigationStyles={Navigator.NavigationBar.Styles} /> } /> ) }Copy the code

InitialRoute: is the root component of the code, that is, after starting the APP, you will see the first screen of the interface, which has three parameters, title: component name, ID: is the unique identifier of the component (string type, is to distinguish the uniqueness of the component and customized), Component: root component.

initialRoute={{title: this._getRootConmponet().title, id: this.state.rootPageKey, component: this._getRootConmponet().component}}
Copy the code

Note: parameter number and parameter name are not fixed, how you define here, decide how to use later.

Navigatorsceneconfigs.js (configureScene) configureScene (configureScene) configureScene (configureScene) RenderScene: Render a scene that returns a component element. Let Component = route.com ponent is take each Component in the route, such as the Component in the initialRoute, after in the configuration, return the Component. SceneStyle: sceneStyle, uniform setting of page offset, can be used for android and iOS navigation bar height inconsistency, also can be used for iPhoneX. NavigationBar: navigationBar property, return a navigationBar type component, navigationBar property is convenient, has a similar to the native transition animation; The disadvantage is that the navigation bar style of the page needs to be customized in its property routeMapper, but not in each page. If the navigation bar style of a page is special, it needs to use the component ID mentioned above to judge, which is relatively high coupling. You can also customize the navigationBar for each page without setting the navigationBar property.

4. Use the navigationBar of the system

NavigationBar:

navigationBar={
    <Navigator.NavigationBar
                            style={{
                                alignItems: 'center'.backgroundColor: '#f8f8f8'.borderBottomWidth:1/PixelRatio.get(),
                                borderBottomColor:'#cccccc',
                            }}
                            routeMapper={RouteMapper}
                            navigationStyles={Navigator.NavigationBar.Styles}
                        />
}
Copy the code

NavigationBar uses only three attributes: style: defines the navigationBar style, background color, bottom line, and subview position. RouteMapper: This is the soul of the navigationBar, and it decides what the navigationBar displays, how it works, etc. NavigationStyles: Android and iOS navigation bar style is different, the Navigator. NavigationBar. Styles will judge what is the current system, the android would return a NavigatorNavigationBarStylesAndroid, IOS is returned NavigatorNavigationBarStylesIOS, behind mentioned adapter iPhoneX, requires NavigatorNavigationBarStylesIOS file changes.

2. RouteMapper: Because routeMapper has a lot of content, it can be extracted and managed in a SEPARATE JS file. Sample code:

module.exports = {

    // Left button
    LeftButton(route, navigator, index, navState) {
        if(index > 0) {
            return( <TouchableOpacity onPress={() => { if (route.backClick) { route.backClick(); Else {navigator.pop() // otherwise, pop}}} style={styles.leftButtonstyle}> <Image source={require('.. /images/trc_pay_pop_btn_back.png')} resizeMode='stretch'/> </TouchableOpacity> ); } else { if (route.id === Config.AccountLoginPage.id) { return ( <TouchableOpacity onPress={() => { if (route.rootBack) Return route.rootback ()}else {trcnativebridge.dismiss (); } }} style={styles.leftButtonStyle}> <Image style={{marginLeft:10}} source={require('.. /images/trc_account_login_close.png')} resizeMode='stretch' /> </TouchableOpacity> ) } else { return ( <View/> ); }}, // RightButton RightButton(route, navigator, index, navState) { if(index > 0 && route.rightButtonTitle) { return ( <TouchableOpacity onPress={() => { if (route. RightBarButtonOnPress) {/ / the above reason the route rightBarButtonOnPress ()}}} style = {styles. RightButtonStyle} > < Text style={styles.rightButtonTextStyle} numberOfLines={1}>{route.rightButtonTitle}</Text> </TouchableOpacity> ); } else {return <View />}}, // Title(route, navigator, index, navState) {let Title = route.title? route.title : ''; return ( <View style={styles.titleBgStyle}> <Text style={styles.middleButtonTextStyle}>{title}</Text> </View> ); }};Copy the code

Code explanation: The routeMapper object has three functions, LeftButton, RightButton, and Title, representing the LeftButton, RightButton, and middle Title, each with parameters (route, navigator, index, navState), They all need to return a component element.

The meanings of each parameter are as follows: route: indicates the current route. Navigator: Indicates the current navigator. Index: indicates the current page position index in the navigation stack. NavState: indicates the current navigation status.

3.LeftButton:

// Left button
    LeftButton(route, navigator, index, navState) {
        if(index > 0) {
            return( <TouchableOpacity onPress={() => { if (route.backClick) { route.backClick(); Else {navigator.pop() // otherwise, pop}}} style={styles.leftButtonstyle}> <Image source={require('.. /images/trc_pay_pop_btn_back.png')} resizeMode='stretch'/> { iOS ? < the Text style = {{marginLeft: - 6, fontSize: accessoryFontSize}} > back < / Text > : null} < / TouchableOpacity >); } else { if (route.id === Config.AccountLoginPage.id) { return ( <TouchableOpacity onPress={() => { if (route.rootBack) Return route.rootback ()}else {trcnativebridge.dismiss (); } }} style={styles.leftButtonStyle}> <Image style={{marginLeft:10}} source={require('.. /images/trc_account_login_close.png')} resizeMode='stretch' /> </TouchableOpacity> ) } else { return ( <View/> ); }}},Copy the code

Code explanation:

  • If index > 0, the current page is not the root view, and the back button is basically a back arrow “<“, or “< back “. Click to return. So we have one hereTouchableOpacityButton. There’s one on itImage. (1) If a page needs to intercept a return event, you can define a route in its componentWillMountbackClickFunction to intercept. The code is as follows:
componentWillMount(){
        this.props.route.backClick = (a)= > {
            Keyboard.dismiss();
            const { navigator } = this.props;
            navigator.pop();
        };
    }
Copy the code

If interception is not required, navigator.pop() is executed directly and the developer does not need to be aware of the return event.

  • If index = 0, the current view’sidIs not the root view. If a page needs to intercept the return event, you can define a route in its componentWillMountrootBackFunction for return interception. (2) If interception is not required, it is executed directlyTRCNativeBridge.dismiss(), tell Native to shut down RN modules.

RightButton and TItle principle and LeftButton similar, will not repeat.

Customize navigationBar

Due to the use of the system’s own navigationBar, will increase the code coupling, is not conducive to later maintenance, so only suitable for the page navigationBar customization less, relatively simple project. If the navigationBar is more customized, such as hiding the navigationBar or adding a search box to the navigationBar, it is better to use the customized navigationBar.

Customization of the navigation bar means writing a common navigation bar component, defining its style, providing attributes and event callbacks for various scenarios, and referencing them on the page that you want (almost every page should be flushed:). Example:

import  NavigatorBar  from './NavigatorBar';
export default class PageClass extends Component {
    render() {
        return (
            <View style={{flex:1}}>
                <NavigatorBar navigator={this.props.navigator}
                              title='SecretGarden'
                              hiddenLeftButton={true}  />
            </View>)}}Copy the code

Advantages: freeStyle can be customized as you want. Disadvantages: ① Use more annoying, each use to import; The transition animation is not very good.

6. IPhoneX

There are many ways to adapt the iPhoneX online. If iPhoneX, it is my method is: put the status bar height increase 24 pixels, namely in the above said NavigatorNavigationBarStylesIOS file, modify STATUS_BAR_HEIGHT, as follows:

var STATUS_BAR_HEIGHT = 20 + (Dimensions.get('window').height === 812 ? 24 : 0); //change by meng, note: for iPhone X
Copy the code

The status bar is just raised, but you also need to shift the top of the page 24 pixels down and the bottom 34 pixels up. Note: Android does not require offsets.

// Top offset
const iOSPaddingTop = 64 + (SCREEN_HEIGHT === 812 ? 24 : 0); // Adapt to iPhone X
const androidPaddingTop = 56;
const paddingTopOffset = global.Android ? androidPaddingTop : iOSPaddingTop;

// Bottom offset
const iOSPaddingBottomOffset = SCREEN_HEIGHT === 812 ? 34 : 0; // Adapt to iPhone X
const androidPaddingBottomOffset = 0;
const paddingBottomOffset = global.Android ? androidPaddingBottomOffset : iOSPaddingBottomOffset;

Copy the code

That completes the iPhoneX adaptation.

Seven, adapt android immersion

Android Immersion is not part of the Navigator section, but navigation is usually discussed in relation to the status bar, so I’ll mention it here.

Immersion is a new feature on Android 5.0. That is, for systems 5.0 or above, you can set the status bar transparent, and the page layout starts from the top of the status bar. For systems below 5.0, the status bar is white on a black background.

Adaptation methods are as follows:

import { StatusBar } from 'react-native';
componentWillMount() {
        if (Android) {
            StatusBar.setBackgroundColor('#f8f8f8');
            StatusBar.setBarStyle('dark-content'.true); }}Copy the code

The background color is set to be consistent with the navigationBar background color.

Eight, prevent fast click multiple push the same interface

On native iOS, it’s common to have a quick one-click button and push the same page twice or more, and it’s also a problem on RN. My solution is to modify the source code of Navigator Navigator. When Navigator pushes, determine whether the ID of the page to be pushed is the same as that of the page at the top of the stack. If not, push; If they are the same, return.

The code is as follows:

push: function(route) {
      //---------- change by meng----------
	  const currentRoutes = this.getCurrentRoutes();
      if (currentRoutes.length > 0) {
          let lastRoute = currentRoutes[currentRoutes.length - 1];
          let oldId = lastRoute.id;
          let newId = route.id;
          if (oldId && newId && oldId === newId) {
              // If it is a continuous push to the same page, it is returned directly
              return; }}//---------- [modify source code end] ----------. }Copy the code

The above is my experience of using Navigator in the development process. My technical level is limited. If there is anything unreasonable or inaccurate, please contact me and correct me.

The original link