On Android phones, the back button is often used as a function, which can be used to return to the previous page, or to cancel an operation, etc. In short, it is very convenient. React Native (RN) project with React Navigation to handle the back button.

React Navigation? As it has become a standard part of RN’s project routing tools, the official RN documentation recommends using Expo for project creation, while Expo recommends using React Navigation for route management.

RN’s back button event

RN provides an API for handling BackHandler, which works much like other event listeners by passing in an event name and an event function, as shown in the following code:

1
2
3
4
Copy the code
  BackHandler.addEventListener('hardwareBackPress', function() {
    // do something
    return true;
  })
Copy the code

Note: This differs from other event listeners in that a return Boolean is required, because the fallback button events are fired in reverse order, i.e. the last registered event is fired first. Return true means that the previous fallback button event is not called, whereas return false means that it is called.

This API is fine to use, but it can be tricky to add events to multiple components if they need to handle the fallback button event at the same time, and to pay attention to the event invocation relationships between components.

In addition, the back button events need to be added and removed in the component lifecycle methods mount and unmount:

One, two, three, four, five, six, sevenCopy the code
  componentDidMount() {
    BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
  }

  componentWillUnmount() {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
  }
Copy the code

However, some level 1 pages are resident in the routing stack, so when a page’s events are added, they will not be destroyed even if the page jumps, causing some performance loss.

React Navigation Processes the rollback event

The React Navigation handles the back button. Here is the official documentation. We can see that the React Navigation handles the back button in much the same way as RN does, but with the life cycle method of the component changed to React Navigation.

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17Copy the code
  constructor(props) {
    super(props);
    this.didFocusSubscription = props.navigation.addListener('didFocus', payload =>
      BackHandler.addEventListener('hardwareBackPress', this.handleBackPress)
    );
  }

  componentDidMount() {
    this.willBlurSubscription = this.props.navigation.addListener('willBlur', payload =>
      BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress)
    );
  }

  componentWillUnmount() {
    this.didFocusSubscription && this.didFocusSubscription.remove();
    this.willBlurSubscription && this.willBlurSubscription.remove();
  }
Copy the code

In the code we can see that React Navigation adds the back button event via the didFocus method (the callback after the page is loaded) and then removes the event via the willBlur method (the callback before the page leaves). This way, even if the page is resident on the routing stack, the back button event is removed whenever the page jumps.

There are still some issues with this approach. First, the page will still load events that cannot be removed, but instead of the back button event, the event will be listened on by React Navigation.

Another serious problem is: If enter and leave the page takes longer (such as the need to send web request) for several times, or switch pages faster, is likely to add the current page events (didFocus) to remove the last page (willBlur), which means that the current page in the back button events may be added immediately after being removed, The operation is abnormal.

A better solution

To summarize the above questions:

  • Needs to be handled in multiple components (pages)The back buttonEvent, which makes writing code cumbersome
  • The sequence of adding and deleting events may be distorted during page switching, resulting in abnormal App behavior

The main reason for the above problems is that the fallback button event is handled in multiple places, which could have been avoided if our APP had only one place to handle the fallback button event.

However, there are two issues that need to be addressed if the handling of the back button event is kept in one place:

  • Where do you add itThe back buttonEvent?
  • How do we determine which page is currently on? Because we need to deal with different logic according to different pages, for example, the back button of page A needs to exit App, while page B needs to return to the previous page.

Where do you add itThe back buttonEvent?

To solve this problem, we can wrap a Layout component in the outermost layer of the component, and then use this component to add the event of the back button, and then use the original root component as the children of this component. Example:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23Copy the code
class Layout extends React.Component { componentDidMount() { BackHandler.addEventListener('hardwareBackPress', this.handleBackPress); } componentWillUnmount() { BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress); } render() { <View> {this.props.children} </View> } } class App extends React.Component { render() { <Layout> <Root /> // Original root component </Layout>}}Copy the code

There is no need to add back button events elsewhere, and all page events are handled in the handleBackPress method.

How to determine which page is the current page?

The React Navigation documentation provides a way to navigate without Navigation parameters. The idea here is to set the root node to get the global React Navigation object, and then call the React Navigation API from that object.

We can extend this method to get the current page by adding the following function:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17Copy the code
Const getCurrentRoute = () => {let route = navigator.state.nav; While (route.routes) {route = route.routes[route.index]; } return route; }; // route: { // "key": "id-1552444588477-2", // "params": { // "disableBack": true, // }, // "routeName": "Settings", // }Copy the code

We can get the route object of the current page, which can be used to determine the page based on routeName:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16Copy the code
handleBackPress = () => { const { routeName } = NavigationSeveice.getCurrentRoute(); switch(routeName) { case 'Home': { // dosomething return true; } case 'Settings': { // dosomething return true; } default: { return true; }}}Copy the code

Direct use of

React Native Android Backer is an RN library based on the same ideas as the React Native Android Backer. You can also use the Readme in the library.

conclusion

This is a practical problems encountered in the RN development, began to refer to the various articles and methods are not very ideal, then found themselves through grope for solution, and its extract became a third-party library, also hope to be able to help to meet the same problem friend, you are welcome to try and leave a message, thank you.