connected-react-router 实现原理

实现流程

  • actions 封装 pushreplacego等主要方法
  • middleware 拦截 actions 信息,触发封装好的 action 方法
  • reducer 新增 router 的state信息
  • ConnectedRouter组件监听路由变化,更新路由信息到 store

依赖库和目录结构

依赖库

目录结构

  • index.js // 入口文件
  • constant.js // actions 常量
  • push.js // 这里只封装history中的push方法,其它大同小异了
  • connectRouter.js // reducer 中同步路由变化的信息
  • routerMiddleware.js // 中间件,拦截 action 触发路由更新
  • ConnectedRouter.js // 组件,主要用途是监听路由变化,提交变化信息到 store

代码分析

index.js

import push from "./push";
import connectRouter from './connectRouter'
import ConnectedRouter from './ConnectedRouter';
import routerMiddleware from './routerMiddleware'

export {
  push,
  connectRouter,
  ConnectedRouter,
  routerMiddleware,
}

constant.js

向外暴露用到的type常量

export const LOCATION_CHANGE = "@@router/LOCATION_CHANGE"; // 路由变化
export const CALL_HISTORY_METHOD = "@@router/CALL_HISTORY_METHOD"; // 触发路由方法

push.js

封装我们的push方法,这里是返回一个action信息,接下来的中间件可以截取到这个action,并触发对应的method操作

import * as TYPES from "./constant";

export default function(path){
  return {
    type: TYPES.LOCATION_CHANGE,
    payload:{
      method:"push",
      path
    }
  }
}

routerMiddleware.js

routerMiddleware是一个中间件,如上面所述,截取action,如果是自己定义的type,拦截并触发对应的路由方法,否则执行下一个中间件

import * as TYPES from "./constant";

export default history => store => next => action =>{
  if (action.type === TYPES.LOCATION_CHANGE) {
    const { method, path } = action.payload;
    history[method](path);
  } else {
    next(action);
  }
}

connectRouter.js

connectRouter就是我们记录信息的reducer了,但是路由state信息需要同步,就需要监听路由的变化,并dispatchstore中。

import * as TYPES from "./constant"

let initState = {}

export default function(history) {
  return (state = initState,action)=>{
    switch (action.type) {
      case TYPES.CALL_HISTORY_METHOD:
        return action.payload;
      default:
        return state;
    }
  }
}

ConnectedRouter.js

ConnectedRouter的目的是为了监听路由变化,并触发store的更新,这样路由就能做到同步更新了。

import * as TYPES from "./constant"
import React, { Component } from 'react'
import { Router } from "react-router";
import { ReactReduxContext }from "react-redux";

export default class ConnectedRouter extends Component {
  static contextType = ReactReduxContext;
  componentDidMount(){
    this.unlisten = this.props.history.listen((payload)=>{
      this.context.store.dispatch({
        type: TYPES.CALL_HISTORY_METHOD,
        payload
      })
    })
  }
  componentWillUnmount(){
    this.unlisten();
  }
  render() {
    const {history,children} = this.props;
    return (
      <Router history={history}>
        {children}
      </Router>
    )
  }
}