技术标签: Redux react redux functional programming compose
Redux 源码系列转眼间就来到最后一篇了,本篇要来介绍最后剩下的两个 API:compose 和 applyMiddleware
compose 函数还是比较常见的,属于函数式编程的标配,作用是将
compose(f1, f2, f3, f4)
变成
f1(f2(f3(f4())))
的调用
然而在 Redux 源码当中其实这个 compose 主要是用于实现 applyMiddleware 时对多个 middleware 的包装
所以接下来我们先看看 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 的核心在于组合多个单参数的函数,然后以最后一个函数的方法签名作为导出函数的方法签名
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))
)
}
相对于 compose 来说,applyMiddleware 才是我们更关注的 api,用于为 store 添加中间件的方法
接下来我们先来看看相关的类型定义
/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
}
很简单,一个是中间件的第一个参数的类型,一个则是中间件的方法签名
接下来我们看看 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 就对了
实际上,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 源码系列就到这里结束啦,下面作者会在继续找找一些常用非常重要的库来带大家解析源码啦~
| Title | Link |
|---|---|
| applyMiddleware - Redux | https://redux.js.org/api/applymiddleware |
| redux - Github | https://github.com/reduxjs/redux |
https://github.com/superfreeeee/Blog-code/tree/main/source_code_research/redux-4.1.1
前面主要介绍了createStore,combineReducers,compose的实现原理,下面,我们看一下 redux中最有意思的中间件部分applyMiddleware。 applyMiddleware代码很简洁,但是含义很广泛。我们来一起看一下: 首先,我们先来重温一下中间件的使用方法: 调用中间件 来看一下createStore的源码 enhancer是中间件,当createStore...
最近在读Redux源码,现将自己实现的代码和理解贴上,后期再补充详细~ 中间件:类似于插件,可以在不影响原本功能、并且不改动原本代码的基础上,对其功能进行增强。在Redux中,中间件主要用于增强dispatch函数。 实现Redux中间件的基本原理,是更改仓库中的dispatch函数。 中间件本身是一个函数,该函数接收一个store参数,表示创建的仓库,该仓库并非一个完整的仓库对象,仅包含getS...
redux是什么、有什么作用,解决什么问题,如果你还不知道,请先去这里: redux中文文档 下面的文章适合对redux有一定理解和使用经验的人 项目github地址:github.com/wangweiange… 如果你觉得对你有帮助的话记得给我一个star呢 applyMiddleware有什么用: 使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式...
在学习了redux过程中,了解到中间件这个名词,但是我看了十遍,也完全就是懵逼的状态。于是又重复敲了几次代码也不能掌握这个东西到底是什么?看官网文档,那么些专业名词,让人半天摸不着头脑。我们从流程图中知道,react组件在执行过程中,特别是在中间插入各种奇怪的需求的时候,不可能每每都是改动一下代码逻辑,而是用一种方便插拔的方式添加进去。但是这个过程说得简单,理解也容易,问题到底是怎么实现的呢?本人...
什么是中间件 在redux中,我们可以在view层dispatch一个action,action到达store中的reducer,reducer根据action的type更新处理state,从而更新store中的数据。但是redux并不能处理异步的dispatch。这时候我们就需要使用redux提供的applyMiddleware对dispatch进行增强,使我们的dispatch可以异步。 这里...
最新内容,请在github阅读。同时,All issue and star welcomed! 1.深入分析redux中的createStore方法 我们首先以一个官方demo来说,代码如下: 下面我们来看看createStore的作用: createStore表示我们创建一个数据库,这个数据库计算下一个状态的方式是通过counter来完成的。而且,如果你要计算下一个状态,需要给我传入当前状态,以...
前言 本文作者站在自己的角度 深入浅出...算了别这么装逼分析 redux applyMiddleware 在设计过程中通过 compose 构建异步数据流的思路。自己假设的一些场景帮助理解,希望大家在有异步数据流并且使用redux的过程中能够有自己的思路(脱离thunk or saga)构建自己的 enhancer.如果你看完本文之后还想对我有更多的了解,可以移步我的github; 正文 实际场...
compose()主要是利用了数组的reduce()方法来实现迭代:第二个函数的执行结果,作为第一个函数的参数。 分析如下: compose(f1,f2,f3) 备注: 函数数组里面,最后一个函数可以有多个参数,其它函数只能有一个参数。...
compose作用:将多个函数组合成一个函数,比如compose(f,g,h),组合成一个f(g(h(...args))) compose传入的函数参数,只有最右边的函数,可以传入多个参数 当compose没有传入参数时,返回args => args 当compose传入参数个数为1时,返回传入的参数 当compose传入参数个数大于1时,返回类似a(b(c(...args))) compo...
关于redux 中间件 在redux中,我们通过dispatch action来更新数据状态,通过reducer来返回新的状态 如果我们想要记录某个状态更新前后的日志,那么我们只需要在此处的dispatch前后做一些处理即可 那如果我们要记录所有状态更新的日志呢?在所有调用disaptch的地方都加上日志处理?这显然不够优雅 但这就是redux中间件要解决的问题 redux中间件内部通过对crea...