基于React Navigation的拦截器封装

本文介绍了如何在ReactNavigation6.x中实现路由拦截器,以优化登录跳转逻辑。通过创建路由表、解析路由、配置拦截器和管理导航跳转,实现了在用户未登录状态下阻止访问特定页面的功能,降低了代码耦合并提高了可维护性。拦截器的设计使得页面只需关注自身业务,而无需关心登录状态的检查。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行环境

React Navigation 6.x
React Native >= 0.63.0
Node > 12

代码参考

npm地址
github地址:lib文件夹为库项目、demo文件夹为示例

React Navigation介绍

React Navigation为React Native提供的路由管理库,利用该库可以很好的管理页面跳转,及导航栏的配置。利用该库可以实现普通的页面跳转,但是在涉及拦截器使用的时候,实现起来就比较麻烦。我们举例说明一下,假如我们程序有如下几个页面

|-- pages
	|-- login.js 登录页面
	|-- record.js 我的订单页面
	|-- buy.js 购买页面
	|-- home.js 首页

我的订单跟购买页面需要依赖用户登录成功才能进入,那么我们常规的操作是通过标识来实现的,如下代码所示
pages/home.js

import React from 'react';
import { Button } from 'react-native';
class Home extends React.Component{
	render(){
	  return (
      <View style={styles.container}>
        <Button title="我的订单" onPress={() => this.props.navigation.navigate('login'), {toPage: "/record"}} />
        <Button title="购买" onPress={() => this.props.navigation.navigate('login'), {toPage: "/buy"}} />
      </View>
    );
	}
}

如上所示,通过传递toPage来标识登录成功后,跳转的页面,如下修改登录页面,pages/login.js

import React from 'react';
import { Button } from 'react-native';
class Login extends React.Component{
	login(){
		...扒拉扒拉一顿登录操作
	    const { toPage } = this.props.route.params;
	    this.props.navigation.navigate(toPage);
	}

	render(){
	  return (
	      <View style={styles.container}>
	        <Button title="我的订单" onPress={this.login} />
	      </View>
      );
	}
}

从以上可以看出,非常繁琐,而且代码非常耦合,有没有更好的解决方案,答案是有的,也就是拦截器的概念,通过配置拦截器让页面只需关系自己业务功能就行了。

如何设计拦截器

先看下React Navigation是怎么配置路由表的,app.js如下所示:

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { Home } from './pages/home';
import { Login } from './pages/login';
import { Record } from './pages/record';
import { Buy } from './pages/buy';
const Stack = createStackNavigator();

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="/home" component={Home} />
        <Stack.Screen name="/login" component={Login} />
        <Stack.Screen name="/record" component={Record} />
        <Stack.Screen name="/buy" component={Buy} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

如何设置路由表

首先,我们需要有个路由表,然后根据路由表解析成React Navigation认识的形式,也就是上面的代码。新增路由表route-config.js,代码如下

import { Home } from './pages/home';
import { Login } from './pages/login';
export default {
	"/home":{
		component: Home
	},
	"/login":{
		component: Login
	}	
}

从上面的路由可以看到路由的路径是以 ‘/’ 开头的,做过Web应该比较熟悉,对以后做三端(Android、iOS、Web)重构也是适配的。

然后解析成React Navigation认识的格式:

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
import routeConfig from './route-config'

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
            {
              Object.keys(routeConfig).map((key) => {
              	  const item = routes[key];
                  return <Stack.Screen name={key} component={item.component} key={key}/>
            }
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

如何配置拦截器

路由表已经有了,接着思考如何加上拦截器,考虑到一个页面有多个拦截器的可能性,所以拦截器应该是一个数组,拦截器在一开始就能确定,所以修改route-config.js,如下所示

import { Home } from './pages/home';
import { Login } from './pages/login';
import { Record } from './pages/record';//我的订单
import { LoginInterceptor } from './interceptors/login-interceptor';//登录拦截器

export default {
	"/home":{
		component: Home
	},
	"/record":{
		interceptors:[
			{
				clazz: LoginInterceptor
			}
		]
		component: Record
	}
}

拦截器写好,以上逻辑为进入我的订单路由 /record 会先检验登录拦截器,也就是判断用户有没有登录,没有登录的话,会先在登录拦截器里面跳转到登录页面。登录拦截器代码如下:

class LoginInterceptor {
  intercept() {
    if(已登录){
      跳转到下一个拦截器或到目标页
    }else {
      跳转到登录页面
    }
  }
}

intercept为拦截函数

如何才能执行拦截器

这一步,首先我们得先用个管理类,来管理包裹React Navigation的跳转方法,等拦截器执行结束在执行跳转到目标页面。所以新增lib/router.js。

export class Router {
	// name为路由如/record,params为参数
	push(name, params){}
}

Router 需要拿到React Navigation的ref,才能跳转,修改app.js。

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import routeConfig from './route-config'
import { navigationRef } from './router';

const Stack = createStackNavigator();

function App() {
  return (
    <NavigationContainer ref={navigationRef}>
      <Stack.Navigator>
            {
              Object.keys(routeConfig).map((key) => {
              	  const item = routes[key];
                  return <Stack.Screen name={key} component={item.component} key={key}/>
            }
      </Stack.Navigator>
    </NavigationContainer>
  );
}

修改lib/router.js,涉及拦截过滤器模式

import { createNavigationContainerRef } from '@react-navigation/native';
import { StackActions, CommonActions } from '@react-navigation/native';
import { FilterManager } from './filter-manager';
import routeResolve from './route-resolve';
import routeConfig from './route-config'

export class Router {
  constructor() {
    this.filterManager = {};
    this.startLen = 0;
  }	

  //跳转函数	
  push(name, params){
      // 声明目标函数
      function targetFun() {
        const pushAction = StackActions.push(name, params);
        navigationRef.dispatch(pushAction);
      }
      this.execute(name, targetFun);
  }
  
  execute(name, targetFun) {
  	
    let route = routeConfig[name];

    if (route) {
      const interceptors = route.interceptors;

      if (interceptors) {
        let interceptorClazzs = [];
        interceptors.forEach(element => {
          interceptorClazzs.push(element["clazz"]);
        });

        const state = navigationRef.getState();
        this.startLen = state ? state.routes.length : 1;

		// 过滤管理器
        this.filterManager = new FilterManager(interceptorClazzs, targetFun);
        this.filterManager.execute();

      } else {
        targetFun();
      }
    }
  }

  /**
   * 执行下一个拦截器
   */
  interceptNext() {
    this.clearStack();
    this.filterManager.execute();
  }

  /**
   * 清栈
   */
  clearStack() {
    navigationRef.dispatch(state => {
      const routes = state.routes.filter((r, index) => {
        return index < this.startLen;
      });

      return CommonActions.reset({
        ...state,
        routes,
        index: routes.length - 1,
      });
    });
  }
}

export const navigationRef = createNavigationContainerRef();
export let router = new Router();

新增过滤管理器 lib/filter-manager.js

/**
 * 拦截过滤管理器
 */
export class FilterManager {
  constructor(interceptorClazzs, targetFun) {
    this.index = 0;
    this.targetFun = targetFun;
    this.interceptorClazzs = interceptorClazzs;
  }

  /**
   * 执行拦截器
   */
  execute(){
    if(this.index == this.interceptorClazzs.length){
      // 执行目标函数	
      this.targetFun();
    }else{
      // 获取下一个拦截器
      let interceptor = new this.interceptorClazzs[this.index]();
      this.index++;
      interceptor.intercept();
    }
  }
}

至此,拦截器的设计就完成了,可以愉快的调用了。

使用router跳转

修改Home页面的跳转,对应的就会调起登录拦截器

import React from 'react';
import { Button } from 'react-native';
import { router } from './lib/router';
class Home extends React.Component{
	render(){
	  return (
      <View style={styles.container}>
        <Button title="我的订单" onPress={() => router.push('/record')} />
        <Button title="购买" onPress={() => router.push('/buy')} />
      </View>
    );
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值