欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-16 15:10:01
...

前言

本文环境基于:

"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

相关标签: rn