React Navigation 监听不同页面的物理返回键事件

前言

本文环境基于:

"react-native": "0.60.5"
"react-navigation": "^4.0.0"
 "typescript": "^3.6.2"

首次编辑时间:2019.9.19

描述

在对应页面添加如下代码我们就能监听不同页面的返回事件。

 backHandler;
  componentDidMount() {
    this.backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      this.onBackButtonPressAndroid, //处理返回事件函数
    );
  }

  componentWillUnmount() {
    this.backHandler && this.backHandler.remove('hardwareBackPress');
  }

当我们使用react-navigation做路由导航时,createStackNavigator+createBottomTabNavigator使用时监听返回键就会有诸多问题:

  1. 多个页面有相同的返回事件,需要给每个页面添加返回事件代码,非常的繁琐冗余。
  2. tabs页面监听返回事件时,虽然只需要给第一个tab添加返回事件代码。但是这样会造成打开的新页面(tabs页面未关闭)也会被监听。

思路

  1. 在根导航器中统一管理,一般来说是App.js/App.tsx/App.ts。
  2. 根据路由属性中路由名称routeName,判断处理对应的事件。

根导航器的没有navigation prop怎么办呢?

可以通过ref访问导航器。并将其传递给我们稍后将使用其进行导航的NavigationService。
不能使用withNavigation,因为它只适用被路由导航包装的子组件。
NavigationService使用具体可以查看官方文档Navigating without the navigation prop
根据官方代码,添加了获取路由名称的方法代码如下:

import {NavigationActions} from 'react-navigation';

let navigator;
//ref传值
const setTopLevelNavigator = navigatorRef => {
  navigator = navigatorRef;
};
//页面跳转方法
const navigate = (routeName, params = {}) => {
  navigator.dispatch(
    NavigationActions.navigate({
      routeName,
      params,
    }),
  );
};
//页面goBack方法
const goBack = () => {
  navigator.dispatch(NavigationActions.back());
};
//获取当前路由
const getCurrentRoute = () => {
  let route = navigator.state.nav;
  while (route.routes) {
    route = route.routes[route.index];
  }
  return route;
};
//获取当前路由名称
const getCurrentRouteName = () => {
  return getCurrentRoute().routeName;
};

export default {
  navigate,
  setTopLevelNavigator,
  getCurrentRoute,
  getCurrentRouteName,
  goBack,
};

示例

有了以上思路我们来处理以下场景:
登陆页面login成功后,进入Main(包含3个tabs),用户在Login和Main页面点击返回键会提示他是否确认退出程序?其余页面则不处理登陆事件。

Main底部导航栏代码

省略部分配置代码。

// 导航栏定义
const TabNavigator = createBottomTabNavigator(
  {
    Home: {
      screen: Home,
      ....
    },
    Bill: {
      screen: Bill,
      ...
    },
    Mine: {
      screen: Mine,
      ...
  },
  ...
);
export default createAppContainer(TabNavigator);

App根导航器代码

const AppNavigator = createStackNavigator(
  {
    Start: Start,
    Login: Login,
    Main: Main, //底部导航栏
    Register: Register,
    Modify: Modify,
  },
  ...
);

const AppContainer = createAppContainer(AppNavigator);

export default class App extends React.Component {
  backHandler;
  componentDidMount() {
    this.backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      this.onBackButtonPressAndroid, //处理返回事件函数
    );
  }

  componentWillUnmount() {
    this.backHandler && this.backHandler.remove('hardwareBackPress');
  }

 onBackButtonPressAndroid = () => {
    const routeName = NavigationService.getCurrentRouteName();
    if (
      //处理Login和三个tabs(Home,Bill,Mine)
      routeName === 'Login' ||
      routeName === 'Home' ||
      routeName === 'Bill' ||
      routeName === 'Mine'
    ) {
      CommUtils.showDialog();
      return true; //true阻止返回键向下传递
    } else {
      return false; //不做任何事情,返回键向下传递,系统默认处理
    }
  };

  public render() {
    return (
      <AppContainer
        ref={navigatorRef => {
          NavigationService.setTopLevelNavigator(navigatorRef);
        }}
      />
    );
  }
}

参考

React Navigation官方文档

react-native-android-backer

你可能感兴趣的:(React,Native)