代码先锋网 代码片段及技术文章聚合

Redux 源码解析(三): compose & applyMiddleware API

技术标签: Redux  react  redux  functional programming  compose

Redux 源码解析(三): compose & applyMiddleware API

前言

Redux 源码系列转眼间就来到最后一篇了,本篇要来介绍最后剩下的两个 API:compose 和 applyMiddleware

正文

1. compose 源码解析

compose 函数还是比较常见的,属于函数式编程的标配,作用是将

compose(f1, f2, f3, f4)

变成

f1(f2(f3(f4())))

的调用

然而在 Redux 源码当中其实这个 compose 主要是用于实现 applyMiddleware 时对多个 middleware 的包装

1.1 方法签名

所以接下来我们先看看 compose 的方法签名

  • /src/compose.ts(阅读笔记:/src/compose.ts/exports.ts
type Func<T extends any[], R> = (...a: T) => R

export default function compose(): <R>(a: R) => R;

export default function compose<F extends Function>(f: F): F;

/* two functions */
export default function compose<A, T extends any[], R>(
  f1: (a: A) => R,
  f2: Func<T, A>
): Func<T, R>;

/* three functions */
export default function compose<A, B, T extends any[], R>(
  f1: (b: B) => R,
  f2: (a: A) => B,
  f3: Func<T, A>
): Func<T, R>;

/* four functions */
export default function compose<A, B, C, T extends any[], R>(
  f1: (c: C) => R,
  f2: (b: B) => C,
  f3: (a: A) => B,
  f4: Func<T, A>
): Func<T, R>;

/* rest */
export default function compose<R>(
  f1: (a: any) => R,
  ...funcs: Function[]
): (...args: any[]) => R;

export default function compose<R>(...funcs: Function[]): (...args: any[]) => R;

这里可以看出 compose 的核心在于组合多个单参数的函数,然后以最后一个函数的方法签名作为导出函数的方法签名

1.2 源码实现

compose 的源码还是比较简单的,过一下就行了

  • /src/compose.ts(阅读笔记:/src/compose.ts/implements.ts
export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return <T>(arg: T) => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args))
  )
}

2. applyMiddleware 源码解析

相对于 compose 来说,applyMiddleware 才是我们更关注的 api,用于为 store 添加中间件的方法

2.1 类型定义

接下来我们先来看看相关的类型定义

  • /src/types/middleware.ts(阅读笔记:/src/types/middleware.ts
import { Dispatch } from './store'

export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
  dispatch: D
  getState(): S
}

export interface Middleware<
  _DispatchExt = {}, // TODO: remove unused component (breaking change)
  S = any,
  D extends Dispatch = Dispatch
> {
  (api: MiddlewareAPI<D, S>): (
    next: D
  ) => (action: D extends Dispatch<infer A> ? A : never) => any
}

很简单,一个是中间件的第一个参数的类型,一个则是中间件的方法签名

2.2 方法签名

接下来我们看看 applyMiddleware 的方法签名

  • /src/applyMiddleware.ts(阅读笔记:/src/applyMiddleware.ts/exports.ts
import compose from './compose'
import { Middleware, MiddlewareAPI } from './types/middleware'
import { AnyAction } from './types/actions'
import {
  StoreEnhancer,
  Dispatch,
  PreloadedState,
  StoreEnhancerStoreCreator
} from './types/store'
import { Reducer } from './types/reducers'

export default function applyMiddleware(): StoreEnhancer;

export default function applyMiddleware<Ext1, S>(
  middleware1: Middleware<Ext1, S, any>
): StoreEnhancer<{ dispatch: Ext1 }>;

export default function applyMiddleware<Ext1, Ext2, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 }>;

export default function applyMiddleware<Ext1, Ext2, Ext3, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>,
  middleware3: Middleware<Ext3, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 }>;

export default function applyMiddleware<Ext1, Ext2, Ext3, Ext4, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>,
  middleware3: Middleware<Ext3, S, any>,
  middleware4: Middleware<Ext4, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 }>;

export default function applyMiddleware<Ext1, Ext2, Ext3, Ext4, Ext5, S>(
  middleware1: Middleware<Ext1, S, any>,
  middleware2: Middleware<Ext2, S, any>,
  middleware3: Middleware<Ext3, S, any>,
  middleware4: Middleware<Ext4, S, any>,
  middleware5: Middleware<Ext5, S, any>
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 & Ext5 }>;

export default function applyMiddleware<Ext, S = any>(
  ...middlewares: Middleware<any, S, any>[]
): StoreEnhancer<{ dispatch: Ext }>;

看起来很复杂,其实本质上就是可以传入多个中间件并封装成一个 enhancer 就对了

2.3 源码实现

实际上,enhancer 名字看起来很高大上,其实没有那么复杂,且听我娓娓道来

  • /src/applyMiddleware.ts(阅读笔记:/src/applyMiddleware.ts/implements.ts
export default function applyMiddleware(...middlewares: Middleware[]): StoreEnhancer<any> {
  return (createStore: StoreEnhancerStoreCreator) =>
    <S, A extends AnyAction>(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>) => {
      const store = createStore(reducer, preloadedState)
      let dispatch: Dispatch = () => {
        // throw error
      }

      const middlewareAPI: MiddlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, ...args),
      }
      const chain = middlewares.map((middleware) => middleware(middlewareAPI))
      dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

      return {
        ...store,
        dispatch,
      }
    }
}

首先定义一个基础的 dispatch 方法,但是禁止中间件在创建的过程就立即调用

      let dispatch: Dispatch = () => {
        // throw error
      }

接下来定义一个 api,负责传递 getState 和 dispatch 方法给每个中间件

      const middlewareAPI: MiddlewareAPI = {
        getState: store.getState,
        dispatch: (action, ...args) => dispatch(action, ...args),
      }

接下来就是将 api 传入中间件并用 compose 串起来

      const chain = middlewares.map((middleware) => middleware(middlewareAPI))
      dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

也就是说当我们执行新的 dispatch 的时候,会由前至后,调用第一个 middleware 的 dispatch,而如果中间件调用 next 才会将 action 传递给下一个中间件来调用

结语

redux 的源码真的是相对简单很多,与网上说的一样非常适合初学者来学习,不过本篇也算是一个特别难的逻辑在于使用 compose 组合中间件的部分,因为中间件是一个有三层参数的科里化函数,还是比较复杂的。

Redux 源码系列就到这里结束啦,下面作者会在继续找找一些常用非常重要的库来带大家解析源码啦~

其他资源

参考连接

TitleLink
applyMiddleware - Reduxhttps://redux.js.org/api/applymiddleware
redux - Githubhttps://github.com/reduxjs/redux

阅读笔记参考

https://github.com/superfreeeee/Blog-code/tree/main/source_code_research/redux-4.1.1

版权声明:本文为weixin_44691608原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44691608/article/details/120051587

智能推荐

Redux源码浅析系列(四):`applyMiddleware`

前面主要介绍了createStore,combineReducers,compose的实现原理,下面,我们看一下 redux中最有意思的中间件部分applyMiddleware。 applyMiddleware代码很简洁,但是含义很广泛。我们来一起看一下: 首先,我们先来重温一下中间件的使用方法: 调用中间件 来看一下createStore的源码 enhancer是中间件,当createStore...

Redux-实现applyMiddleware

最近在读Redux源码,现将自己实现的代码和理解贴上,后期再补充详细~ 中间件:类似于插件,可以在不影响原本功能、并且不改动原本代码的基础上,对其功能进行增强。在Redux中,中间件主要用于增强dispatch函数。 实现Redux中间件的基本原理,是更改仓库中的dispatch函数。 中间件本身是一个函数,该函数接收一个store参数,表示创建的仓库,该仓库并非一个完整的仓库对象,仅包含getS...

redux v3.7.2源码解读与学习之 applyMiddleware

redux是什么、有什么作用,解决什么问题,如果你还不知道,请先去这里: redux中文文档 下面的文章适合对redux有一定理解和使用经验的人 项目github地址:github.com/wangweiange… 如果你觉得对你有帮助的话记得给我一个star呢 applyMiddleware有什么用: 使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式...

Redux之中间件applyMiddleware源码解读

在学习了redux过程中,了解到中间件这个名词,但是我看了十遍,也完全就是懵逼的状态。于是又重复敲了几次代码也不能掌握这个东西到底是什么?看官网文档,那么些专业名词,让人半天摸不着头脑。我们从流程图中知道,react组件在执行过程中,特别是在中间插入各种奇怪的需求的时候,不可能每每都是改动一下代码逻辑,而是用一种方便插拔的方式添加进去。但是这个过程说得简单,理解也容易,问题到底是怎么实现的呢?本人...

从源码角度理解Redux中间件---applyMiddleware

什么是中间件 在redux中,我们可以在view层dispatch一个action,action到达store中的reducer,reducer根据action的type更新处理state,从而更新store中的数据。但是redux并不能处理异步的dispatch。这时候我们就需要使用redux提供的applyMiddleware对dispatch进行增强,使我们的dispatch可以异步。 这里...

猜你喜欢

redux的reateStore,combineReducers,bindActionCreators,applyMiddleware源码分析

最新内容,请在github阅读。同时,All issue and star welcomed! 1.深入分析redux中的createStore方法 我们首先以一个官方demo来说,代码如下: 下面我们来看看createStore的作用: createStore表示我们创建一个数据库,这个数据库计算下一个状态的方式是通过counter来完成的。而且,如果你要计算下一个状态,需要给我传入当前状态,以...

深度剖析 redux applyMiddleware 中 compose 构建异步数据流的思路

前言 本文作者站在自己的角度 深入浅出...算了别这么装逼分析 redux applyMiddleware 在设计过程中通过 compose 构建异步数据流的思路。自己假设的一些场景帮助理解,希望大家在有异步数据流并且使用redux的过程中能够有自己的思路(脱离thunk or saga)构建自己的 enhancer.如果你看完本文之后还想对我有更多的了解,可以移步我的github; 正文 实际场...

Redux compose()方法解析:

compose()主要是利用了数组的reduce()方法来实现迭代:第二个函数的执行结果,作为第一个函数的参数。 分析如下: compose(f1,f2,f3) 备注: 函数数组里面,最后一个函数可以有多个参数,其它函数只能有一个参数。...

Redux源码分析--compose

compose作用:将多个函数组合成一个函数,比如compose(f,g,h),组合成一个f(g(h(...args))) compose传入的函数参数,只有最右边的函数,可以传入多个参数 当compose没有传入参数时,返回args => args 当compose传入参数个数为1时,返回传入的参数 当compose传入参数个数大于1时,返回类似a(b(c(...args))) compo...

Redux applyMiddleWare 使用Redux中间件

关于redux 中间件 在redux中,我们通过dispatch action来更新数据状态,通过reducer来返回新的状态 如果我们想要记录某个状态更新前后的日志,那么我们只需要在此处的dispatch前后做一些处理即可 那如果我们要记录所有状态更新的日志呢?在所有调用disaptch的地方都加上日志处理?这显然不够优雅 但这就是redux中间件要解决的问题 redux中间件内部通过对crea...