在上一篇中我们了解到,更新Redux中状态的流程是这种:action -> reducer -> new state。

文中也讲到。action是一个普通的javascript对象、reducer是一个普通的方法。在reducer中依据当前的state、接收到的action来生成一个新的state以达到更新状态的目的。

那么问题来了。每次action被触发(dispatch)。reducer就会同步地对store进行更新,在实际开发项目的时候,有非常多需求都是须要通过接口等形式获取异步数据后再进行更新操作的。怎样异步地对store进行更新呢?

还是拿上一篇文中写的计数器来做样例,如今须要加入一个按钮。点击这个按钮的时候会在1秒之后对counter的值进行加1操作。

最简单的方法当然是把store.dispatch({type: ‘INCREASE’})放到一个setTimeout的回调里去运行:

document.getElementById('btn_async_increase').addEventListener('click', function () {
setTimeout(function () {
store.dispatch({type: 'INCREASE'});
}, 1000);
});

可是这事实上还是一次同步的更新操作,并非我们想要的。(在dispatch之后的store更新还是同步的)

使用中间件

Redux本身提供的Api与功能并不多,可是它提供了一个中间件(插件)机制,能够使用第三方提供的中间件或自己编写一个中间件来对Redux的功能进行增强,比方能够使用redux-logger这个中间件来记录action以及每次action前后的state、使用redux-undo来取消/重做action、使用redux-persist-store来对store进行持久化等等。

很多其它的中间件及相关工具能够查看这里

要实现异步的action。有多个现成的中间件可供选择,这里选择官方文档中使用的redux-thunk这个中间件来实现。

安装ReduxThunk

首先当然须要先获取redux-thunk,在一般的项目里使用npm i redux-thunk -S来安装到项目中,在codepen里仅仅需简单地设置一下外部js文件引入就能够了。

使用applyMiddleware挂载中间件

在创建store的时候。我们将ReduxThunk使用Redux.applyMiddleware方法进行包装后传给Redux.createStore的第二个方法:

const {createStore, applyMiddleware} = Redux;

const store = createStore(counter, applyMiddleware(ReduxThunk.default));

异步action

原来的store.dispatch方法仅仅能接收一个普通的action对象作为參数。当我们加入了ReduxThunk这个中间件之后。store.dispatch还能够接收一个方法作为參数,这种方法会接收到两个參数,第一个是dispatch。等同于store.dispatch,第二个是getState,等同于store.getState,也就是说。如今能够这样来触发INCREASE:

store.dispatch((dispatch, getState) => dispatch({type: 'INCREASE'}));

点击按钮一秒后运行dispatch:

<div>
<p>Count: <span id="value">0</span></p>
<button id="btn_increase">+ 1</button>
<button id="btn_async_increase">+ 1 async</button>
<button id="btn_decrease">- 1</button>
</div>
document.getElementById('btn_async_increase').addEventListener('click', function () {
store.dispatch((dispatch, getState) => {
setTimeout(() => {
dispatch({type: 'INCREASE'});
}, 1000);
});
});

看一下效果(CodePen):

See the Pen <a href="http://codepen.io/awaw00/pen/wgbjBY/">wgbjBY</a> by aaron wang (<a href="http://codepen.io/awaw00">@awaw00</a>) on <a href="http://codepen.io">CodePen</a>.

ReduxThunk解析

依托于ReduxThunk我们实现了异步触发action的功能,那么ReduxThunk是怎么做到的呢?

我们来看看ReduxThunk的代码。ReduxThunk一共仅仅有一个代码文件,14行代码:

https://github.com/gaearon/redux-thunk/blob/master/src/index.js

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;

我们使用的是默认导出的thunk,也就是createThunkMiddleware()运行的结果。

createThunkMiddleware方法里返回了一个奇怪的东西:

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

换一种写法:

function ({dispatch, getState}) {
return function (next) {
return function (action) {
...
};
};
}

事实上就是一个多层嵌套返回函数的函数,使用箭头的写法在函数式编程中叫做柯里化。对柯里化更具体的介绍能够看一看这篇张鑫旭的博客

第一个(最外层)方法的參数是一个包括dispatch和getState字段(方法)的对象。事实上就是store对象。所以也能够写成:

store => next => action => {
...
};

这事实上就是一个Redux中间件的基本写法

參数next是一个方法,这种方法的作用是通知下一个Redux中间件对这次的action进行处理: next(action),假设一个中间件中没有运行next(action),则action会停止向兴许的中间件传递。并阻止reducer的运行(store将不会由于本次的action而更新)。

參数action就不用多说了,就是当前被触发的action。

在ReduxThunk这个中间件中,做的处理非常easy:推断当前的action是否为一个方法。假设是,就运行action这种方法,并将store.dispatchstore.getState方法作为參数传递给action方法;假设不是,则运行next(action)将控制权转移给下一个中间件(假设有)。

所以当我们给store.dispatch方法传入一个方法的时候。ReduxThunk就会去运行这种方法,以达到自由控制action触发流程的一个目的。

ReudxThunk在实际项目中的应用

上面我们给计数器写的异步action仅仅是为了作演示,简单地使用setTimeout来触发action。以下给出一个比較贴近现实的样例。

在某个项目中。我们须要依据一个userId来调用后端接口获取这个用户的具体信息并存储到Redux store中:

function getUserDetail (userId) {
return (dispatch, getState) => {
if (getState().user.id === userId) {
// store中的user已经为当前的目标user,无需反复获取
return;
} dispatch({type: 'USER_DETAIL_REQUEST', payload: userId});
fetch(`${API_ROOT}/user/${userId}`)
.then(res => res.json())
.then(res => {
// 触发SUCCESS的action后在reducer中更新user数据
dispatch({type: 'USER_DETAIL_REQUEST_SUCCESS', payload: res});
})
.catch(err => dispatch({type: 'USER_DETAIL_REQUST_FAILURE', payload: err})
};
} // 获取userId为10000的用户详情
store.dispatch(getUserDetail(10000));

【React全家桶入门之十三】Redux中间件与异步action的更多相关文章

  1. 【React全家桶入门之十】登录与身份认证

    细致想想,我们的后台系统还没有一个登录功能,太不靠谱,赶紧把防盗门安上! SPA的鉴权方式和传统的web应用不同:因为页面的渲染不再依赖服务端,与服务端的交互都通过接口来完毕,而REASTful风格的 ...

  2. React全家桶入门

    http://blog.csdn.net/column/details/14545.html

  3. react全家桶从0搭建一个完整的react项目(react-router4、redux、redux-saga)

    react全家桶从0到1(最新) 本文从零开始,逐步讲解如何用react全家桶搭建一个完整的react项目.文中针对react.webpack.babel.react-route.redux.redu ...

  4. webpack4 中的最新 React全家桶实战使用配置指南!

    最新React全家桶实战使用配置指南 这篇文档 是吕小明老师结合以往的项目经验 加上自己本身对react webpack redux理解写下的总结文档,总共耗时一周总结下来的,希望能对读者能够有收获, ...

  5. 使用React全家桶搭建一个后台管理系统

    引子 学生时代为了掌握某个知识点会不断地做习题,做总结,步入岗位之后何尝不是一样呢?做业务就如同做习题,如果‘课后’适当地进行总结,必然更快地提升自己的水平. 由于公司采用的react+node的技术 ...

  6. React全家桶+Material-ui构建的后台管理系统

    一.简介 一个使用React全家桶(react-router-dom,redux,redux-actions,redux-saga,reselect)+Material-ui构建的后来管理中心. 二. ...

  7. 使用react全家桶制作博客后台管理系统

    前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基于react全家桶(React.React-r ...

  8. react-music React全家桶项目,精品之作!

    React-Music 全家桶项目,精品之作! 一.简介 该项目是基于React全家桶开发的一个音乐播放器,技术栈采用:Webpack + React + React-redux + React-ro ...

  9. 初学者的React全家桶完整实例

    概述 该项目还有些功能在开发过程中,如果您有什么需求,欢迎您与我联系.我希望能够通过这个项目对React初学者,或者Babel/webpack初学者都有一定的帮助.我在此再强调一下,在我写的这些文章末 ...

随机推荐

  1. bzoj 1312 最大密度子图

    晕,m=0是要输出1(弄的我还找管理员要数据,但明显题意是叫我们输出0呀) 最大密度子图,把边转换成点,然后二分答案,跑最大权闭合子图判定是否可行. #include <cstdio> # ...

  2. BZOJ 2330 SCOI2011糖果 差分约束

    2330: [SCOI2011]糖果 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2819  Solved: 820 题目连接 http://www ...

  3. 新型穿墙监控雷达Range-R:让你的隐私无所遁形(转)

    还是隐私问题,原帖地址:http://www.freebuf.com/news/57446.html 在我们的认知中,政府对民众的监控已经成为一种常态.从电话.电子邮件到通信聊天.社交网络,一切细节都 ...

  4. 使用注册表优化终端、编辑器的中英字体混合显示,如「Consolas + 雅黑」「Monaco + 雅黑」

    在终端.cmd.编辑器中偶尔会有中文字符出现,Windows下默认的点阵字体「宋体」和等宽英文字符放在一起非常违和.一个解决方法是下载混合字体,比如「Consolas + YAHEI hybrid」, ...

  5. golang 字符串与整数, 布尔转换 strconv

    strconv 是golang对于字符串和基本数据类型之间的转换字符串转整数testStr := "1000" testInt, err := strconv.Atoi(testS ...

  6. Save a 32-bit Bitmap as 1-bit .bmp file in C#

    What is the easiest way to convert and save a 32-bit Bitmap to a 1-bit (black/white) .bmp file in C# ...

  7. C#中使用NLua z

    直接下载NLua编译好的版本在c#项目中使用,运行的时候会提示无法加载lua52.dll,但lua52.dll这个文件又是在运行目录下的. 其实NLua不是无法加载lua52.dll本身,而是找不到l ...

  8. Linux架构和目录-基础篇

    1.Linux目录结构 2. /boot/ 存放系统内核文件,如vmlinuz,initrd,System.map等.其中, a. vmlinuz是可引导的.压缩的内核,“vm”即“Virtual M ...

  9. pytest文档24-fixture的作用范围(scope)

    fixture作用范围 fixture里面有个scope参数可以控制fixture的作用范围:session > module > class > function fixture( ...

  10. Java 8 的新特性和改进总览

    这篇文章是对Java 8中即将到来的改进做一个面向开发者的综合性的总结,JDK的这一特性将会在2013年9月份发布. 在写这篇文章的时候,Java 8的开发工作仍然在紧张有序的进行中,语言特新和API ...