【源码分析】redux-thunk
前言
前面学习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)
中间件的思想
在不使用中间件时:
使用了中间件之后:
派发给
store
的action
会经过中间件一层层处理,最终才会到达store
。中间件的顺序跟action
的处理顺序是密切相关的,只有前面的中间件完成任务,后面的中间件才有机会继续处理action
。每个中间件都有自己的“熔断”处理,当它认为这个 action 不需要后面的中间件进行处理时,后面的中间件也就不能再对这个 action 进行处理了。
中间件基本架构
前面我们学习写logger
中间件的时候提到,中间件函数的基本架构是这样的:
const middlewarea = ({dispatch, getState}) => (next) => (action) => {next(action);
}
中间件函数接受两个参数,分别是dispatch
和getState
,该函数返回的函数接收一个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相关推荐
- koa源码分析-co模块以及thunk
Thunk以及CO模块 co4.0之前都是返回的thunk函数 之后的都是返回promise thunk thunk:在 JavaScript 语言中,Thunk 函数替换的是将多参数函数,替换成单参 ...
- redux middleware 源码分析
原文链接 middleware 的由来 在业务中需要打印每一个 action 信息来调试,又或者希望 dispatch 或 reducer 拥有异步请求的功能.面对这些场景时,一个个修改 dispat ...
- redux源码分析之一:createStore.js
欢迎关注redux源码分析系列文章: redux源码分析之一:createStore.js redux源码分析之二:combineReducers.js redux源码分析之三:bindActionC ...
- Redux源码分析(一)
Redux源码分析(createStore) 使用redux都快3年了,到现在也没认真去了解一下源码罪过啊,所以需要对它进行一些分析和学习,一方面能更好的去使用它,另一方面也学习一下该框架的设计思路, ...
- redux源码分析之二:combineReducers.js
欢迎关注redux源码分析系列文章: redux源码分析之一:createStore.js redux源码分析之二:combineReducers.js redux源码分析之三:bindActionC ...
- Redux源码分析--Enhancer
Redux源码分析: Redux源码分析--Middleware(1) Redux源码分析--Middleware(2) Redux源码分析--Enhancer Redux源码分析--createSt ...
- Koa源码分析(二) -- co的实现
Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...
- Vuex 2.0 源码分析
作者:滴滴公共前端团队 - 黄轶 大家好,我叫黄轶,来自滴滴公共前端团队,我们团队最近写了一本书 --<Vue.js 权威指南>,内容丰富,由浅入深.不过有一些同学反馈说缺少 Vuex 的 ...
- react-redux源码分析及实现原型(下)
上一次我们讲解了Provider.connect.selectorFactory.这次主要分析 connectAdvanced 这个核心API. react-redux源码分析及实现原型_上 conn ...
- SingleSpa及qiankun入门、源码分析及案例
文章目录 SingleSpa及qiankun入门.源码分析及案例 一.简介 1.微服务 2.什么是微前端 3.微前端的优点 4.微前端的缺点 5.如何落地微前端 6.示例 7.总结 二.SingleS ...
最新文章
- 前端学python有什么用-对于一个小白,前端和python哪个更适合?
- linux netlink 编程示例(二)应用层
- 大话设计模式(三 复制VS复用)
- 简述回源原理和CDN常见多级缓存
- flink读不到kafka数据问题
- 绝杀《绝地求生》外挂!
- SecureCRT软件下载及注册方法-附带软件及软件注册机
- mysql卸载安装pxc_PXC安装
- VPS安装msf教程
- Matlab中loglog函数使用
- python symbols函数_Python的武器库07:sympy模块
- 解决XP IIS连接访问人数限制的问题
- Aptos Move虚拟机中出现首个严重漏洞
- 半导体器件物理【1】量子理论扫盲——从Planck到态叠加原理
- 《数据分析实战》--用R做多元回归分析
- 【Proteus仿真】BCD码转十进制(74HC42)
- 关于hibernate中invers跟cascade的一点看法
- 苹果mac版微软官方远程连接工具下载Microsoft Remote Desktop For Mac
- vue render 渲染函数 属性写法
- 批量比较两个PDF文档(PDFUtil,通过文本或者图像进行比较)第一篇
热门文章
- java web随机生成四则运算_java四则运算生成器
- 海口计算机中专学院,海口市第一职业中学专业_2021年海口市第一职业中学招生专业_中专专业_2020年技校专业_技校网...
- Java 集合List遍历删除
- AsyncTask详解
- wep前端入门01_概念概述与HTML基础标签
- 百度收录批量查询 如何批量查询百度年月日的收录数量
- Win10蓝牙不可用,设备管理器也找不到蓝牙驱动的解决办法
- ZZULIOJ.1123: 最佳校友
- 校园网ARP攻击的防御
- 计算机维护基础知识ppt,日常维护和计算机基础知识.ppt