redux-middleware的实现

博客地址

compose 函数开始

在函数式编程当中有一个很重要的概念就是函数组合,实际上就是把处理数据的函数像管道一样连接起来,然后让数据穿过管道得到最终的结果。


    const add = x => x + 2  // 加
    const minus = x => x - 2    // 减
    const mul = x => x * 2  // 乘
    const div = x => x / 2  // 除

    minus(mul(add(3))) // => 8

如果我想对一个参数执行上面的 add, mul, minus 函数, 然后得到返回值, 上面的写法可读性就会很差, 这时候我们就需要一个 compose 函数, 它可以接受上面的函数作为参数, 然后返回一个函数, 返回的这个函数也可以达成上面的效果, 如下


    ...
    const myOperate1 = compose(minus, mul, add) // 加 => 乘 => 减
    const myOperate2 = compose(div, add, mul) // 乘 => 加 => 除

    myOperate1(3) // => 8
    myOperate2(3) // => 4

关于 compose 很多函数编程库里面都有实现, 这里我们不考虑一些特殊情况, 对于这个需求自己简单实现一下大概类似这样

    const compose = (...args) => args.reduce( (pre, d) => num => pre(d(num)) )

ScriptOJ (一个关于 Web 前端开发评测系统的网站) 上也有这样一道题, 可以自己去测试下。

redux 的中间件

我们之前有去手动实现 redux 异步, 其实也算有一些简单的中间件, 最终目的就是改造了 storedispatch 函数。但是当时自己纯粹为了实现目的,代码可读性和延展性几乎没有。用过 nodejskoa 框架的一定知道中间件这个概念。redux 这里也是使用了中间件的思想, 用来增强 reduxdispatch 函数。

redux 规定的 middleware 格式


// redux 中间件 middleware 的格式
export default store => next => action => {
    console.log('dispatch: ', action)
    next()
    console.log('finish: ', action)
}

上面的代码我们可以感受很多函数编程的思想(这也是我喜欢 react 一个很重要的原因), 函数编程中有一个重要的概念 柯里化, 其实我们之前去实现 compose 函数时候细心的同学也能发现, 作为 compose 函数的参数, 这些函数应该都只接受一个参数, redux 规定的中间件写法是一个链式返回函数的写法, 中间的参数都已经固定了, 也就是我们只能在最后的代码框里书写中间件的业务逻辑, 在这个里面我们可以拿到哪些参数使用呢, 很明显就是上面链式返回函数里的参数。

action 我们很容易想到 dispatch 函数的参数就叫做 action, 但其实这里拿到的 action 应该是我们原始的内容经过 reduxmiddleware 一层一层处理到当前中间件时候的 action, 有了 action 我们要怎么处理它, 我们最原始的 dispatch 函数去哪里了, storenext 又指的是什么呢?带着问题我们继续看他后面是怎么处理的。

中间件各个参数含义


// redux 对 middleware 的处理
...
const store = createStore(...args)
let dispatch = store.dispatch
let chain = []

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

这里 middlewares 就是我们创建 store 时候传入的中间件数组, 这里先是对 middlewares map 循环传入参数 middlewareAPI 得到返回值。 这里middleware 传入的第一个参数 store 已经找到了, 我们可以在 store 上取到 getStatedispatch 这两个函数。那么 next 函数又是传入的呢, 这里我们看到了熟悉的 compose 函数了, 我们来看看这里 compose 函数的实现。


// 处理中间件的 compose 函数
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

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

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

这里的 funcs 指的就是上面的 chain 数组, 这里多了对于中间件个数的判断, 最后通过 reduce 的实现已经和我们开篇讲的非常相似了。这里同样是把这些函数功能叠加起来, a 相当于排在前买呢对 middleware, b 则是靠后的 middleware , 可以看到 a 执行的参数传入的是 b(...args), 所以对于 a 来说它获取到的 next 就是经过 b 处理过的 dispatch

有时候抽象的东西总是难以理解, 我们这里假设传入中间件一共有三个【我们熟悉的 thunk, promsie 和 logger】, 所以这里执行完效果应该如下

  • logger 而言 next --> dispatch
  • promise 而言 next --> logger 处理 - dispatch
  • thunk 而且 next --> promise 处理 - logger 处理 - dispatch

这里注意 middlewareAPI 中的 dispatch 熟悉并没有简单的就赋值为 dispatch, 而是包装成一个函数,当函数调用时去调用 dispatch, 而后面 dispatch 会被重新赋值, 所以中间件通过参数 store 拿到的 dispatch的功能应该等同于包装后的 dispatch.

redux-logger 中有这样一句提示:Note: logger must be the last middleware in chain, otherwise it will log thunk and promise, not actual actions, 希望你把 logger 这个中间件作为最后一个参数, 正是因为放在前面的中间件有可能会改变 action ,最后这个最贴近原始 dispatch 位置的中间件拿到的 action 基本都是用户最终想要的实际类型。

思考

学习 react 的过程中, 对 函数式编程 有了新的认识, 让之前很多只停留在了解层面的知识点有了实际应用的地方。感觉很多概念理解起来会比较抽象, 但是仔细研究后又会觉得很优雅。

参考

时间: 2024-10-27 23:19:08

redux-middleware的实现的相关文章

Redux系列x:源码解析

写在前面 redux的源码很简洁,除了applyMiddleware比较绕难以理解外,大部分还是 这里假设读者对redux有一定了解,就不科普redux的概念和API啥的啦,这部分建议直接看官方文档. 此外,源码解析的中文批注版已上传至github,可点击查看.本文相关示例代码,可点击查看. 源码解析概览 将redux下载下来,然后看下他的目录结构. npm install redux 这里我们需要关心的主要是src目录,源码解析需要关心的文件都在这里面了 index.js:redux主文件,主

函数式编程在Redux/React中的应用

本文简述了软件复杂度问题及应对策略:抽象和组合;展示了抽象和组合在函数式编程中的应用;并展示了Redux/React在解决前端状态管理的复杂度方面对上述理论的实践.这其中包括了一段有趣的Redux推导. 软件复杂度及其应对策略 软件复杂度 软件的首要技术使命是管理复杂度.--代码大全 在软件开发过程中,随着需求的变化和系统规模的增大,我们的项目不可避免地会趋于复杂.如何对软件复杂度及其增长速率进行有效控制,便成为一个日益突出的问题.下面介绍两种控制复杂度的有效策略. 对应策略 抽象 世界的复杂.

图解redux和react的关系

最近研究了下redux项目里的example里面的目录结构,发现里面文件夹比较多,概念也比较多,所以画图整理一下: 以examples中的real-world为例,文件结构长这个样子 先简单看一下redux和react的数据流: redux ┌──────────────────────────┐ │ │ │ │ │ ┌────────────────────┐ │ │ Reducers │ │ └────────────────────┘ │ ▲ ▼ │ ┌────────────────┐

[译] Redux 异步四兄弟

本文讲的是[译] Redux 异步四兄弟, 原文地址:Redux 4 Ways 原文作者:本篇文章已获得作者 Nader Dabit 授权 译文出自:掘金翻译计划 译者:reid3290 校对者:rccoder,xekri 在十分钟内实践 Thunk . Saga . Observable 以及 Redux Promise Middleware. 在上一次的 React Native online meetup 活动中,笔者就 Thunk . Saga 以及 Redux Observable 之

一种简化 Redux 的思路

动机 我们热爱 React 和 Redux.但是,Redux 中有太多的样板文件,需要很多的重复劳动,这一点令人沮丧:更别提在实际的 React 应用中,还要集成 react-router 的路由功能了. 一个典型的 React/Redux 应用看起来像下面这样: actions.js export const ADD_TODO = 'todos/add' export const COMPLETE_TODO = 'todos/complete' export function addTodo(

[译] Redux 有多棒?

本文讲的是[译] Redux 有多棒?, 原文地址:What's So Great About Redux? 原文作者:Justin Falcone 译文出自:掘金翻译计划 本文永久链接:https://github.com/xitu/gold-miner/blob/master/TODO/whats-so-great-about-redux.md 译者:ZiXYu 校对者:MJingv, calpa Redux 有多棒? Redux 能够优雅地处理复杂且难以被 React 组件描述的状态交互.

使用Redux管理你的React应用

React是最好的前端库,因为其发源于世界上最好的后端语言框架. -信仰 4.0 will likely be the last major release. Use Redux instead. It's really great. -Flummox框架作者 acdliteAndrew Clark 为什么使用React还需要使用别的框架来搭配? React的核心是使用组件定义界面的表现,是一个View层的前端库,那么在使用React的时候我们通常还需要一套机制去管理组件与组件之间,组件与数据模

Redux异步方案选型

作为react社区最热门的状态管理框架,相信很多人都准备甚至正在使用Redux. 由于Redux的理念非常精简,没有追求大而全,这份架构上的优雅却在某种程度上伤害了使用体验:不能开箱即用,甚至是异步这种最常见的场景也要借助社区方案. 如果你已经挑花了眼,或者正在挑但不知道是否适合,或者已经挑了但不知道会不会有坑,这篇文章应该适合你. 本文会从一些常见的Redux异步方案出发,介绍它们的优缺点,进而讨论一些与异步相伴的常见场景,帮助你在选型时更好地权衡利弊. 简单方案 redux-thunk:指路

Redux 入门教程(二):中间件与异步操作

上一篇文章,我介绍了 Redux 的基本做法:用户发出 Action,Reducer 函数算出新的 State,View 重新渲染. 但是,一个关键问题没有解决:异步操作怎么办?Action 发出以后,Reducer 立即算出 State,这叫做同步:Action 发出以后,过一段时间再执行 Reducer,这就是异步. 怎么才能 Reducer 在异步操作结束后自动执行呢?这就要用到新的工具:中间件(middleware). 一.中间件的概念 为了理解中间件,让我们站在框架作者的角度思考问题: