前言

前面学习redux时,学到了applyMiddleware云里雾里,所以这次学习一下redux-thunk的源码,希望有助于深入理解applyMiddleware的源码实现。

redux-thunk的作用

redux中我们使用dispatch(action)时,action必须是一个简单对象,但是如果我们希望dispacth时可以进行各种逻辑处理,比如异步操作时,此时action会是一个函数,那么我们就需要借助redux-thunk这样的中间件了。

export changeList = (data) =>({type: 'CHANGE_LIST',data
})export const getList = (id) => {return async(dispatch, getState) => {const state = getState();const bookName = state.bookName;let res = await axios.get('xxxx?id' + id + '&bookName=' + bookName);dispatch(changeList(res.data));}
}dispacth(getList)

中间件的思想

在不使用中间件时:


使用了中间件之后:

派发给storeaction会经过中间件一层层处理,最终才会到达store。中间件的顺序跟action的处理顺序是密切相关的,只有前面的中间件完成任务,后面的中间件才有机会继续处理action。每个中间件都有自己的“熔断”处理,当它认为这个 action 不需要后面的中间件进行处理时,后面的中间件也就不能再对这个 action 进行处理了。

中间件基本架构

前面我们学习写logger中间件的时候提到,中间件函数的基本架构是这样的:

const middlewarea = ({dispatch, getState}) => (next) => (action) => {next(action);
}

中间件函数接受两个参数,分别是dispatchgetState,该函数返回的函数接收一个next类型的参数,如果调用了内部这个函数,那么代表中间件完成了自己的职能,并将控制权交给下一个中间件。即(action) => next(action)会被交给下一个中间件。

(action) => {}这个函数可以进行多种操作:

  • 调用dispatch派发一个新的action对象;
  • 调用getState获得当前store上的其他状态;
  • 调用next告诉redux当前中间件调用完毕,可以调用下一个中间件了;
  • 访问action对象的所有数据。

redux-thunk的源码

function createThunkMiddleware(extraArgument) {return ({ dispatch, getState }) => (next) => (action) => {if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}return next(action);};
}const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;export default thunk;

内部判断action的类型,如果是非函数类型,那么直接next(action)调用下个中间件,如果是函数类型,那么先把action这个函数执行,其中action的参数分别为dispatch、getState和arguments

到这里我们是不是还是觉得很抽象,接下来我们结合applyMiddleware来分析一下。

理解执行过程

上一节我们说到createStore内部是返回一个store对象,如果在内部遇到enhancer,即我们使用了中间件的话,那么就直接返回enhancer的执行结果,所以enhancer执行之后的函数必须为store对象。

// ...
return enhancer(createStore)(reducer, preloadedState);

再加上

const store = createStore(reducer, preloadedState, applyMiddleware(thunk))

那么现在可以确定的是applyMiddleware函数的基本架构应该是这样的:

export const applyMiddleware = (...middlewreas){return (createStore) => (reducer, preloadedState) => {const store = createStore(reducer, preloadedState);// 处理dispatch,并将新的dispatch替换store中的dispacthreturn store;}
}

如何处理dispatch,我们继续看:

export default function applyMiddleware(...middlewares){return (createStore) => (reducer, preloadedState) => {const store = createStore(reducer, preloadedState)// 如果在中间件构造过程中调用,抛出错误提示let dispatch = () => {throw new Error('Dispatching while constructing your middleware is not allowed. ' +'Other middleware would not be applied to this dispatch.')}const middlewareAPI = {getState: store.getState,dispatch: (action, ...args) => dispatch(action, ...args)}// 存放多个middleware调用的结果const chain = middlewares.map(middleware => middleware(middlewareAPI))// 用compose整合chain数组,并赋值给dispatch// compose(f1, f2, f3) = compose(f1(f2(f3())))dispatch = compose(...chain)(store.dispatch)// 将新的dispatch替换原先的store.dispatchreturn {...store,dispatch}}
}

我们尝试来捋一遍当执行applyMiddleware(thunk))的时候会发生什么:

// const thunk = createThunkMiddleware();
// 即
thunk = ({ dispatch, getState }) => (next) => (action) => {if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}return next(action);
};
// 在applyMiddlewrea中会执行
// const chain = middlewares.map(middleware => middleware(middlewareAPI))// 即
const reduxFn = (next) => (action) => {if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}return next(action);
}
const chain = [reduxFn];
// 在applyMiddlewarea中继续执行
// dispatch = compose(...chain)(store.dispatch)// 前面对compose的源码进行过分析,此时即为:
dispatch = ((next) => (action) => {if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}return next(action);
})(store.dispatch)// 即使用了redux-thunk后,增强了dispacth的能力,既可以接受简单对象,也可以接受函数
dispacth = (action) => {if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}// 这个next即dispatchreturn next(action)
}// 所以如果action不是一个函数的话,就直接执行
return dispatch(action)// 如果action是一个函数的话,那么就运行这个函数
return action(dispatch, getState, extraArgument);
// 前面我们写了这个栗子
export const getList = (id) => {return async(dispatch, getState) => {// 获得store中其他stateconst state = getState();const bookName = state.bookName;let res = await axios.get('xxxx?id' + id + '&bookName=' + bookName);dispatch(changeList(res.data));}
}// 此时如果执行dispacth(getList)的话,那么就是
(async(dispatch, getState) => {const state = getState();const bookName = state.bookName;let res = await axios.get('xxxx?id' + id + '&bookName=' + bookName);dispatch(changeList(res.data));
})(dispatch, getState)

总结

redux-thunk的源码真的好短!从源码理解来说不太难,但是结合applyMiddleware来跑一边,还是有点绕。这次通过看源码,发现了自己之前开发的时候,年前不懂事,不知道函数action内部可以通过getState()获得store的值…现在学到了。

参考

  • 深入理解Redux中间件
  • 我的源码阅读之路:redux源码剖析

【源码分析】redux-thunk相关推荐

  1. koa源码分析-co模块以及thunk

    Thunk以及CO模块 co4.0之前都是返回的thunk函数 之后的都是返回promise thunk thunk:在 JavaScript 语言中,Thunk 函数替换的是将多参数函数,替换成单参 ...

  2. redux middleware 源码分析

    原文链接 middleware 的由来 在业务中需要打印每一个 action 信息来调试,又或者希望 dispatch 或 reducer 拥有异步请求的功能.面对这些场景时,一个个修改 dispat ...

  3. redux源码分析之一:createStore.js

    欢迎关注redux源码分析系列文章: redux源码分析之一:createStore.js redux源码分析之二:combineReducers.js redux源码分析之三:bindActionC ...

  4. Redux源码分析(一)

    Redux源码分析(createStore) 使用redux都快3年了,到现在也没认真去了解一下源码罪过啊,所以需要对它进行一些分析和学习,一方面能更好的去使用它,另一方面也学习一下该框架的设计思路, ...

  5. redux源码分析之二:combineReducers.js

    欢迎关注redux源码分析系列文章: redux源码分析之一:createStore.js redux源码分析之二:combineReducers.js redux源码分析之三:bindActionC ...

  6. Redux源码分析--Enhancer

    Redux源码分析: Redux源码分析--Middleware(1) Redux源码分析--Middleware(2) Redux源码分析--Enhancer Redux源码分析--createSt ...

  7. Koa源码分析(二) -- co的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

  8. Vuex 2.0 源码分析

    作者:滴滴公共前端团队 - 黄轶 大家好,我叫黄轶,来自滴滴公共前端团队,我们团队最近写了一本书 --<Vue.js 权威指南>,内容丰富,由浅入深.不过有一些同学反馈说缺少 Vuex 的 ...

  9. react-redux源码分析及实现原型(下)

    上一次我们讲解了Provider.connect.selectorFactory.这次主要分析 connectAdvanced 这个核心API. react-redux源码分析及实现原型_上 conn ...

  10. SingleSpa及qiankun入门、源码分析及案例

    文章目录 SingleSpa及qiankun入门.源码分析及案例 一.简介 1.微服务 2.什么是微前端 3.微前端的优点 4.微前端的缺点 5.如何落地微前端 6.示例 7.总结 二.SingleS ...

最新文章

  1. 前端学python有什么用-对于一个小白,前端和python哪个更适合?
  2. linux netlink 编程示例(二)应用层
  3. 大话设计模式(三 复制VS复用)
  4. 简述回源原理和CDN常见多级缓存
  5. flink读不到kafka数据问题
  6. 绝杀《绝地求生》外挂!
  7. SecureCRT软件下载及注册方法-附带软件及软件注册机
  8. mysql卸载安装pxc_PXC安装
  9. VPS安装msf教程
  10. Matlab中loglog函数使用
  11. python symbols函数_Python的武器库07:sympy模块
  12. 解决XP IIS连接访问人数限制的问题
  13. Aptos Move虚拟机中出现首个严重漏洞
  14. 半导体器件物理【1】量子理论扫盲——从Planck到态叠加原理
  15. 《数据分析实战》--用R做多元回归分析
  16. 【Proteus仿真】BCD码转十进制(74HC42)
  17. 关于hibernate中invers跟cascade的一点看法
  18. 苹果mac版微软官方远程连接工具下载Microsoft Remote Desktop For Mac
  19. vue render 渲染函数 属性写法
  20. 批量比较两个PDF文档(PDFUtil,通过文本或者图像进行比较)第一篇

热门文章

  1. java web随机生成四则运算_java四则运算生成器
  2. 海口计算机中专学院,海口市第一职业中学专业_2021年海口市第一职业中学招生专业_中专专业_2020年技校专业_技校网...
  3. Java 集合List遍历删除
  4. AsyncTask详解
  5. wep前端入门01_概念概述与HTML基础标签
  6. 百度收录批量查询 如何批量查询百度年月日的收录数量
  7. Win10蓝牙不可用,设备管理器也找不到蓝牙驱动的解决办法
  8. ZZULIOJ.1123: 最佳校友
  9. 校园网ARP攻击的防御
  10. 计算机维护基础知识ppt,日常维护和计算机基础知识.ppt