前言

刚来公司的时候,对react项目中的thunk中间件的作用一直不太了解,最近有时间决定好好研究一下。鉴于本人初次写博客,并已假设读者已掌握redux的一些基本用法;如有错误,还望指出。不胜感激!

首先简单回顾一下redux工作流程

图画的不太好,见谅;
对于reactUI组件来说,数据的来源无外乎两种,一种是用户主动触发的动作,例如点击事件、提交表单,输入操作;另一种是组件主动的数据更新,如获取页面初始数据,子组件接受父组件的props变化而进行更新视图操作;
如图所示,无论那种对于数据的操作,对于view都会派发出一个action

状态的更新

正如我们所知,在redux里,每次更新后的Store都会对应着一个新的view,而Store里面数据的更新依赖action的触发————Store.dispatch(action)会自执行初始化中createStore中注入的reducers,从而计算出新的状态。

import { createStore } from 'redux'
//reducer 计算状态的纯函数
//initialState 初始化数据
//enhancers中间件
createStore(reducers, initialState, enhancers)

action的使用和插件的扩展

对于组件的输入操作(如点击事件),可以将store.dispatch(action)绑定到组件

 const store = createStore(reducer);const TodoList = ({ state, someActionCreator }) => (<ul>{state.map(someState =><Todokey={someState.someData}onClick={() => store.dispatch(someActionCreator(state.someData))}/></ul>)

或者通过connect方法,从组件的props中拿到dispatch方法,发出一个action

  //  将state注入到组件的props里//  注意,这里的state指的是redux管理的数据,每一个view的状态对应着//   唯一的state;//   state的集合就是redux管理的store
const mapStateToProps = store => ({state: store.state
})
​//  将action注入到组件的props 里
const mapDispatchToProps = dispatch => ({actions: state => dispatch(actionCreators(state))
})
​
export default connect(mapStateToProps,mapDispatchToProps
)(TodoList)

然后组件绑定事件就可以改成这样 ,( actionCreators用于生成action, 参考官方链接 https://redux.js.org/basics/actions)

 const TodoList = ({ state, actions }) => (`<ul>{state.map(someState =><Todokey={someState.someData}onClick={() => actions(someState.someData)}/></ul>`)

那么问题来了,dispatch是同步执行reducers生成新状态的,对于页面的操作没有问题;但是如果点击事件是请求了某个结果,需要等待结果响应后再更新视图呢?应该如何处理?

因而redux引入了thunk中间件,对action进行了扩展

##thunk中间件解决了什么问题?
引入thunk插件后,我们可以在actionCreators内部编写逻辑,处理请求结果。而不只是单纯的返回一个action对象。

//未引入前的写法
let nextTodoId = 0
export const addTodo = text => ({type: 'ADD_TODO',id: nextTodoId++,text})//引入thunk后let nextTodoId = 0export const addTodo = text => ({return async dispatch => {//dosomething, requestawait request()dispatch({type: 'ADD_TODO',id: nextTodoId++,text})}})

thunk中间件的使用方法

import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';const store = createStore(reducer,applyMiddleware(thunk)
);

createStore其实可以接受三个参数,第二个参数preloadedState一般作为整个应用的初始化数据,如果传入了这个参数,applyMiddleware就会被当做第三个参数处理

const store = createStore(reducer,initialState,applyMiddleware(thunk)
);

中间件都要放到applyMiddleware里,如果要添加中间件,可以依次添加,但是要遵循文档定义的顺序

const store = createStore(reducer,initialState,applyMiddleware(thunk,middleware1, middleware2)
);

源码解读

也许你会奇怪,为什么使用的时候要按照上面的写法,那我们就一起看下方法的实现

首先是createStore的参数顺序
function createStore(reducer, preloadedState, enhancer) {var _ref2;if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {enhancer = preloadedState;preloadedState = undefined;}if (typeof enhancer !== 'undefined') {if (typeof enhancer !== 'function') {throw new Error('Expected the enhancer to be a function.');}return enhancer(createStore)(reducer, preloadedState);}if (typeof reducer !== 'function') {throw new Error('Expected the reducer to be a function.');}

第一个判断已经告诉了我们答案,参数的类型检验结果决定了顺序

applyMiddleware是干什么用的
function applyMiddleware() {for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {middlewares[_key] = arguments[_key];}return function (createStore) {return function () {for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {args[_key2] = arguments[_key2];}var store = createStore.apply(undefined, args);var _dispatch = function dispatch() {throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');};var middlewareAPI = {getState: store.getState,dispatch: function dispatch() {return _dispatch.apply(undefined, arguments);}};var chain = middlewares.map(function (middleware) {return middleware(middlewareAPI);});_dispatch = compose.apply(undefined, chain)(store.dispatch);return _extends({}, store, {dispatch: _dispatch});};};
}

代码不多,而且非常清晰:
1、applyMiddleware顾名思义,用于调用各种中间件;
2、applyMiddleware执行后,将所有入参中间件存入一个数组,并且返回一个闭包(闭包的概念不做累述)
3、闭包接受一个createStore作为入参并且执行后返回下一个闭包,createStore这个入参有没有很眼熟,没错,就是redux的createStore。

返回结果

返回将所有中间件串联存入的dispatch,执行时从右向左执行,第一次的执行结果会返回给一下个,依次类推。

如何实现每个中间件串联执行

_dispatch = compose.apply(undefined, chain),使用了一个compose函数,调用之后就可以将所有中间件串联起来,那么compose又是如何实现的呢?

精华所在

function compose() {for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {funcs[_key] = arguments[_key];}if (funcs.length === 0) {return function (arg) {return arg;};}if (funcs.length === 1) {return funcs[0];}return funcs.reduce(function (a, b) {return function () {return a(b.apply(undefined, arguments));};});
}

个人认为这个compose函数是整个redux中非常亮眼的部分,短短几行代码,就完成了一个核心功能的扩展,是责任链设计模式的经典体现。

ALSO 我们也可以使用这个compose方法对applyMiddleware进行扩展

let devtools = () => noop => {console.log(noop);return noop;   //createStore};
const enhancers = [applyMiddleware(...middleware),devtools()];
createStore(reducers, initialState, compose(...enhancers));

然后回来,我们就明白了createStore中的设计

//如果存在中间件参数,那么将会得到一个经过改装的dispatch
// return _extends({}, store, {dispatch: _dispatch});
if (typeof enhancer !== 'undefined') {if (typeof enhancer !== 'function') {throw new Error('Expected the enhancer to be a function.');}return enhancer(createStore)(reducer, preloadedState);}
dispatch经过了怎样的改装

如上已经说过,compose会将传入的函数数组从右向左串联执行

compose.apply(undefined, chain)(store.dispatch);

thunk一定会接受上一个中间件的执行结果继续执行,然后最终在createState里返回一个改造好的dispatch, 接下来我只要看下thunk是怎样实现的,就了解了整个中间件使用的原理:

function createThunkMiddleware(extraArgument) {return function (_ref) {var dispatch = _ref.dispatch,getState = _ref.getState;return function (next) {//最终的dispatch//next就是接收的store.dispatch参数,为上一个中间件改造过的dispatchreturn function (action) {if (typeof action === 'function') {return action(dispatch, getState, extraArgument);}return next(action);};};};
}var thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;export default thunk;

代码同样精炼,改造后的dispatch入参接受的数据类型:
1、非function,不处理,将action 传给下一个中间件,最终都会根据传入的action计算相应的reducers(开头说的自执行)————store.dispatch(action)
2、function类型的action, 自动触发函数,并且将store.dispatch传入

总结

再结合开始介绍的thunk用法,我们就明白了thunk的原理,可以在actionCreators里通过返回一个函数,然后就可以在函数里编写某些异步操作了,待异步操作结束,最后通过传入的store.dispatch,发出action通知给Store要进行状态更新。

对于react-thunk中间件的简单理解相关推荐

  1. Redux 异步数据流-- thunk中间件源码解析

    Thunk 引入背景 这是一个关于Redux异步数据流的故事.引入thunk中间件的完整故事在Redux官方中文文档异步数据流.一句话总结就是:原生Redux只支持同步数据流,所以需要引入中间件(mi ...

  2. 教你简单理解分布式与传统单体架构的区别

    教你简单理解分布式与传统单体架构的区别 原文:教你简单理解分布式与传统单体架构的区别 分布式是一种系统架构方式,而在分布式系统中一般基于中间件进行开发,消息中间件是分布式系统中比较核心的中间件之一.这 ...

  3. [react] 说说你对React的渲染原理的理解

    [react] 说说你对React的渲染原理的理解 1.单向数据流.React是一个MVVM框架,简单来说是在MVC的模式下在前端部分拆分出数据层和视图层.单向数据流指的是只能由数据层的变化去影响视图 ...

  4. React中diff算法的理解

    React中diff算法的理解 diff算法用来计算出Virtual DOM中改变的部分,然后针对该部分进行DOM操作,而不用重新渲染整个页面,渲染整个DOM结构的过程中开销是很大的,需要浏览器对DO ...

  5. 浏览器解析jsx_简单理解JavaScript,TypeScript和JSX

    原标题:简单理解JavaScript,TypeScript和JSX Java: 基本概念: Java一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为Java引 ...

  6. 项目架构相关知识的个人简单理解(水平有限,勿喷)

    (一)传统架构 一台Web应用服务器Tomcat并发量为400,如果当并发量为40000时,理论上需要100台: 同一个工程部署到多台服务器上就会存在两个问题: 问题1: 在Tomcat集群中节点数量 ...

  7. android 点击事件消费,Android View事件分发和消费源码简单理解

    Android View事件分发和消费源码简单理解 前言: 开发过程中觉得View事件这块是特别烧脑的,看了好久,才自认为看明白.中间上网查了下singwhatiwanna粉丝的读书笔记,有种茅塞顿开 ...

  8. react构建淘票票webapp,及react与vue的简单比较。

    前言 前段时间使用vue2.0构建了淘票票页面,并写了一篇相关文章vue2.0构建淘票票webapp,得到了很多童鞋的支持,因此这些天又使用react重构了下这个项目,目的无他,只为了学习和共同进步! ...

  9. 【转载】Deep learning:十九(RBM简单理解)

    Deep learning:十九(RBM简单理解) 这篇博客主要用来简单介绍下RBM网络,因为deep learning中的一个重要网络结构DBN就可以由RBM网络叠加而成,所以对RBM的理解有利于我 ...

最新文章

  1. 挡d挡切换_吉行贴士 | 从N-D换挡,就是如此轻松
  2. C++中null与0的区别
  3. c语言font6x8,爱字体下载安卓版-iFont爱字体下载V5.9.8.6 安卓手机版-全新的手机字体软体西西软件下载...
  4. SQL SERVER 参考:游标(Cursor)的讲解与实例
  5. python listdir报错_Python常见十六个错误集合,你知道那些?
  6. python统计数据分析基础教程_Python数据分析基础教程:NumPy学习指南(第2版)
  7. 深入理解 Nginx 之架构篇
  8. Win7安装 Rational Rose软件
  9. ueditor常见用法
  10. 使用STM8S003F3P6的硬件I2C读写AT24C16的EEPROM的经验心得
  11. Ring3触发BSOD代码实现及内核逆向分析
  12. SEO需要每天写文章吗?
  13. R语言实战 ---学习心得08
  14. 笔记本当服务器显示屏,笔记本当服务器屏幕设置
  15. c语言建立小根堆的算法,小根堆(Heap)的详细实现
  16. 畅言插件,http换成https失效了
  17. 移动端 自适应布局方案
  18. 公司注册流程需要哪些步骤
  19. 现在在网上卖什么东西赚钱,分享4种适合网销的产品!
  20. 精确的目标检测中定位置信度的获取

热门文章

  1. 混合开发入门,java jdk和android sdk安装配置+模拟器安装配置+android studio运行起项目--windows电脑
  2. SV660 PCB 伺服驱动器PCB,原理图,和BOM
  3. Tegaki安装指南
  4. 安徽大学计算机学硕复试线,安徽大学2020年考研复试分数线
  5. Java 代理模式的实现和原理详细分析
  6. Chakra TypedArray代码实现笔记
  7. NTP网络时间服务器(北斗授时设备)应用平安城市天网工程
  8. 商业企业管理学类毕业论文文献有哪些?
  9. rx570显卡linux驱动,华硕EX-RX570-O4G驱动下载_ASUS EX-RX570-O4G显卡驱动下载-硬件之家...
  10. 大数据分析思路的4点心得