爆改connected-react-router 支持immer

829 阅读3分钟

这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

背景

学习前端三个月了,准备刷刷面试题,总结总结,一天几道面试题,向大厂进军。

最近在学 React 项目搭建,在github上查看了很多项目中都使用到了 connected-react-router,我很纳闷,这是个什么东东? 我新的React项目模板是不是可以用到!

connected-react-router

connected-react-router 作用:

  1. 核心是实现路由和 redux 仓库的同步
  2. 可以在 action creator 里面通过派发动作的方式跳转路径
  3. 可以页面路径发生变化的时候把路由信息放到仓库中去

俗话:由于我们React项目一般会搭配redux使用,所以connected-react-router的作用就是把history同步到redux中,我们可以通过和其他redux存值一样通过store获取。

四个组成部分, 分两类

  1. push 是生成派发路径变化的 action

  2. routerMiddleware 是接受此 action 来真正跳转路径

  3. ConnectedRouter 监听路径变化, 当路径发生变化的时候,向仓库派发动作要改变仓库里状态里 router 里的 pathname action

  4. connectRouter 接受这个动作后, 由它来修改仓库里的状态里的 router 里的 pathname action

为什么要修改 connected-react-router

由于我们的React项目使用的是immer库,而connected-react-router不支持,只支持immutable.js。

至于为什么选择immer库,是因为immutable库太大,操作数据各种set不是特别方便。

怎样修改 connected-react-router

image.png

看看这个库怎么支持immutable的

image.png

竟然引入了redux-immutable,以及connected-react-router/immutable。

redux-immutable我们可以不用考虑,这里只是用到了combineReducers。这个方法的作用是把多个reducer合并成一个reducer。

我们使用的immer,直接使用redux自带的就可以。

主要看connected-react-router/immutable。我们下载源码,看看我们是否可以借鉴,修改一下,支持immer。

我们先来看看源码目录:

image.png

再来看看immutable.js文件

image.png

我们先来看怎样把history存储在reducer,我们可以看到上面是通过connectRouter。

image.png

那我们先看看connectRouter 是怎么原理:

export const connectRouter = /*#__PURE__*/ createConnectRouter(immutableStructure)

我们可以从代码中看到是调用了createConnectRouter方法,接受对象。

image.png

我们通过代码可以看到,createConnectRouter方法的作用其实就是initRouterState,然后返回一个reducer,只不过这个方法内部是通过fromjs存储对象。这个是immutable的方法。根本就是接受action,存值,取值。

那我们在我们的项目中实现这个不就可以了,只是调用不同的Api(immer)。


import { createHashHistory } from 'history';
import produce from 'immer';
import { LOCATION_CHANGE } from 'connected-react-router';
import { IActionParam } from '@/types/IRedux';

let history = createHashHistory();
export default history;

//查看connected-react-router 的connectRouter 方法,使immer与history 结合使用
export interface HistoryState {
  location: any;
  action: any;
}

//解析location,只是取值不一样。
const injectQuery = location => {
  if (location && location.query) {
    // Don't inject query if it already exists in history
    return location;
  }

  const searchQuery = location && location.search;

  if (typeof searchQuery !== 'string' || searchQuery.length === 0) {
    return {
      ...location,
      query: {},
    };
  }

  // Ignore the `?` part of the search string e.g. ?username=codejockie
  const search = searchQuery.substring(1);
  // Split the query string on `&` e.g. ?username=codejockie&name=Kennedy
  const queries = search.split('&');
  // Contruct query
  const query = queries.reduce((acc, currentQuery) => {
    // Split on `=`, to get key and value
    const [queryKey, queryValue] = currentQuery.split('=');
    return {
      ...acc,
      [queryKey]: queryValue,
    };
  }, {});

  return {
    ...location,
    query,
  };
};

//初始化reducer
const initHistoryState: HistoryState = {
  location: injectQuery(history.location),
  action: history.action,
};

/* eslint-disable no-param-reassign  */
//生成reducer ,接受action,保存数据,存储使用的是produce方法
export const reducer = produce((draft: HistoryState, actionParam: IActionParam) => {
  if (actionParam.type === LOCATION_CHANGE) {
    const { location, action } = actionParam.payload;
    draft.action = action;
    draft.location = injectQuery(location);
    return draft;
  }
  return draft;
}, initHistoryState);

那么我们现在就完美的实现了history的reducer。

后面的就比较简单了!

export const ConnectedRouter = /#PURE/ createConnectedRouter(immutableStructure)

这个createConnectedRouter 方法是通用的,只是immutableStructure 是immutable。

我们看看实现:

image.png

其实就是写了几个方法 fromJS,getIn,merge,toJS。

这里由于immer的存值和取值都和普通js对象一样,我们只需要看普通js怎么实现即可。

image.png

基本都是返回原值,就是实现了一个getIn

image.png

这不我们也可以实现immer的ConnectedRouter的组件了!

结语

一步一步慢慢来,踏踏实实把活干!