你为什么需要异步操作? https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux  

  在redux基础篇的介绍中,我们介绍了redux的基本概念, 对于state的改变有了详尽的了解,但是并没有提到异步问题如何解决? 何为异步? Action 发出以后,Reducer 立即算出 State,这叫做同步;Action 发出以后,过一段时间再执行 Reducer,这就是异步

  在vue中对异步的实现是在actions下使用,如下所示:

 getDefaultAddress ({commit, state}) {
return new Promise(function (resolve, reject) {
axios.get('/bbg/user/get_default_address', {
params: {
uid: localStorage.getItem("uid")
}
}).then(function (response) {
if (response.data.code == ) {
console.log("获取默认地址成功");
// 存储默认地址
commit(UPDATE_DEFAULT_ADDRESS, response.data.data);
}
resolve();
});
});
},

  这里是一个异步的操作,即首先执行getDefaultAddress, 然后在执行这个的过程中,我们需要等到返回结果之后再去改变state数据,而vue的方法就是在判断成功的使用commit一个reducer,这样,就可以完成异步的操作了。

  那么react中是如何实现这种异步操作呢?  这时就需要使用工具: 中间件了。

  

一、中间件的概念

  为了理解中间件,让我们站在框架作者的角度思考问题:如果要添加功能,你会在哪个环节添加?

()Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
()View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
()Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。

  想来想去,只有发送 Action 的这个步骤,即store.dispatch()方法,可以添加功能。举例来说,要添加日志功能,把 Action 和 State 打印出来,可以对store.dispatch进行如下改造。

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action);
next(action);
console.log('next state', store.getState());
}

  

  上面代码中,store.dispatch进行了重定义在发送 Action 前后添加了打印功能。这就是中间件的雏形

  中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。

个人理解:

将具体业务和底层逻辑解耦的组件。

大致的效果是:
需要利用服务的人(前端写业务的),不需要知道底层逻辑(提供服务的)的具体实现,只要拿着中间件结果来用就好了。

举个例子:
我开了一家炸鸡店(业务端),然而周边有太多屠鸡场(底层),为了成本我肯定想一个个比价,再综合质量挑选一家屠鸡场合作(适配不同底层逻辑)。由于市场变化,合作一段时间后,或许性价比最高的屠鸡场就不是我最开始选的了,我又要重新和另一家屠鸡场合作,进货方式、交易方式等等全都要重来一套(重新适配)。

然而我只想好好做炸鸡,有性价比高的肉送来就行。于是我找到了一个专门整合屠鸡场资源的第三方代理(中间件),跟他谈好价格和质量后(统一接口),从今天开始,我就只需要给代理钱,然后拿肉就行。代理负责保证肉的质量,至于如何根据实际性价比,选择不同的屠鸡场,那就是代理做的事了。

  即中间件实际上就是在某两个步骤之间承担一部分任务,完成某个功能,这就是中间件。

  

二、 中间件的用法

  本教程不涉及如何编写中间件,因为常用的中间件都有现成的,只要引用别人写好的模块即可。比如,上一节的日志中间件,就有现成的redux-logger模块。这里只介绍怎么使用中间件

  

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

  

  即先从redux中import使用中间件的插件,然后就可以在创建store的时候使用中间件了。 当然,中间件也是需要提前引入的。

  上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。

  补充:我们怎么知道 redux 模块是否含有 applyMiddleware 和 createStore模块呢?  我们可以在 npm install redux --save 之后在node-modules中找到redux,然后在redux中找到入口文件,接着进一步找到内层文件,找到 export , 我们在es/utils/index.js中可以看到导出文件如下:

  export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose };

  这样,引入applyMiddleware中间件就没有任何问题了。

  需要注意的是:(1)createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。

const store = createStore(
reducer,
initial_state,
applyMiddleware(logger)
);

  (2)中间件的次序有讲究。

const store = createStore(
reducer,
applyMiddleware(thunk, promise, logger)
);

上面代码中,applyMiddleware方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放在最后,否则输出结果会不正确

 

三、applyMiddlewares()

  上面我们使用了applyMiddlewares()方法,这个方法的作用就是使用中间件,之前,我们也已经找到其源码所在位置,现在粘贴如下所示:

  

export default function applyMiddleware() {
for (var _len = arguments.length, middlewares = Array(_len), _key = ; _key < _len; _key++) {
middlewares[_key] = arguments[_key];
} return function (createStore) {
return function (reducer, preloadedState, enhancer) {
var store = createStore(reducer, preloadedState, enhancer);
var _dispatch = store.dispatch;
var chain = []; var middlewareAPI = {
getState: store.getState,
dispatch: function dispatch(action) {
return _dispatch(action);
}
};
chain = middlewares.map(function (middleware) {
return middleware(middlewareAPI);
});
_dispatch = compose.apply(undefined, chain)(store.dispatch); return _extends({}, store, {
dispatch: _dispatch
});
};
};
}

  即这里可以接受任意多的中间件,然后将所有中间件放在一个middlewares数组中。  接着返回一个函数,使用中间件,在阮一峰老师的博客中也有此段源代码,写法有不同之处,只是这里是es5他的是es6的语法。

  

四、异步操作的基本思路

  理解了中间件以后,就可以处理异步操作了。

同步操作只要发出一种 Action 即可,异步操作的差别是它要发出三种 Action

操作发起时的 Action
操作成功时的 Action
操作失败时的 Action

  以向服务器取出数据为例,三种 Action 可以有两种不同的写法。

// 写法一:名称相同,参数不同
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } } // 写法二:名称不同
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }

  除了 Action 种类不同,异步操作的 State 也要进行改造,反映不同的操作状态。下面是 State 的一个例子。

let state = {
// ...
isFetching: true,
didInvalidate: true,
lastUpdated: 'xxxxxxx'
};

  

五、 redux-thunk 中间件

  这个中间件就可以解决异步actions的多次action触发问题。

六、 redux-promise中间件

  既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。

  即此中间件解决的问题和redux-thunk中间件解决的问题相似。

redux进阶 --- 中间件和异步操作的更多相关文章

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

    上一篇文章,介绍了 Redux 的基本做法:用户发出 Action,Reducer 函数算出新的 State,View 重新渲染. 但是,一个关键问题没有解决:异步操作怎么办?Action 发出以后, ...

  2. Redux 中间件和异步操作

    回顾一下Redux的数据流转,用户点击按钮发送了一个action,  reducer 就根据action 和以前的state 计算出了新的state, store.subscribe 方法的回调函数中 ...

  3. Redux的中间件原理分析

    redux的中间件对于使用过redux的各位都不会感到陌生,通过应用上我们需要的所有要应用在redux流程上的中间件,我们可以加强dispatch的功能.最近也有一些初学者同时和实习生在询问中间件有关 ...

  4. React:快速上手(7)——使用中间件实现异步操作

    React:快速上手(7)——使用中间件实现异步操作 本文参考链接:Stack Overflow redux-thunk 我们使用store.dispath进行派发时,只能传递一个普通对象进去,如下: ...

  5. Redux的中间件Middleware不难,我信了^_^

    Redux的action和reducer已经足够复杂了,现在还需要理解Redux的中间件.为什么Redux的存在有何意义?为什么Redux的中间件有这么多层的函数返回?Redux的中间件究竟是如何工作 ...

  6. (六)Redux进阶

    1 UI组件与容器组件的拆分 UI组件(傻瓜组件):只负责页面显示,没有任何逻辑 容器组件(聪明组件):并不去管UI到底长成什么样,关注的是整个业务逻辑 2 无状态组件 一个普通的函数就是无状态组件 ...

  7. 17. react redux的中间件

    1. redux 数据流程图 View 会派发一个 Action Action 通过 Dispatch 方法派发给 Store Store 接收到 Action 连同之前的 State 发给  Red ...

  8. Redux thunk中间件

    redux-thunk https://github.com/reduxjs/redux-thunk Why Do I Need This? Thunks are the recommended mi ...

  9. Redux进阶(像VUEX一样使用Redux)

    更好的阅度体验 前言 redux的问题 方案目标 如何实现 思考 前言 Redux是一个非常实用的状态管理库,对于大多数使用React库的开发者来说,Redux都是会接触到的.在使用Redux享受其带 ...

随机推荐

  1. Send [1] times, still failed

    com.alibaba.rocketmq.client.exception.MQClientException: Send [1] times, still failed, cost [696094] ...

  2. Git简明使用指南[转]

    git - 简易指南 助你开始使用 git 的简易指南,木有高深内容,;). Tweet 作者:罗杰·杜德勒 感谢:@tfnico, @fhd and Namics 其他语言 english, deu ...

  3. 试题 H: 人物相关性分析 第十届蓝桥杯

    试题 H: 人物相关性分析时间限制: 1.0s 内存限制: 512.0MB 本题总分: 20 分[问题描述]小明正在分析一本小说中的人物相关性.他想知道在小说中 Alice 和 Bob有多少次同时出现 ...

  4. JObject使用

    1.首先需要引用Json.NET\Newtonsoft.Json.dll程序集. 2.Page页 function saveUser() { var param = { id: , name: '张三 ...

  5. Solr中的一些查询参数

    fl: 是逗号分隔的列表,用来指定文档结果中应返回的 Field 集.默认为 “*”,指所有的字段. defType: 指定query parser,常用defType=lucene, defType ...

  6. MSSQL中通过关键字查找所有存储过程

    select b.namefrom 数据库名.dbo.syscomments a, 数据库名.dbo.sysobjects bwhere a.id=b.id and b.xtype='p' and a ...

  7. 基于Quartz.net的远程任务管理系统 二

    紧接着上一篇.上一篇讲了表设计与ADO.Net基本操作.接下来,就来说说怎么动态来添加Job任务及清理过期任务吧. 首先,先理一下思路,做事情要先把思绪理清了,然后下手就快准狠了.下面是我的思路:做一 ...

  8. C# defualt关键字默认值用法

    默认值表达式生成类型的默认值. 默认值表达式在泛型类和泛型方法中非常有用. 使用泛型类和泛型方法时出现的一个问题是,如何在无法提前知道以下内容的情况下将默认值赋值给参数化类型 T: T 是引用类型还是 ...

  9. 【.net】Dictionary<TKey, TValue>源码分析

    一图胜过千言万语~

  10. cesium编程入门(四)界面介绍及小控件隐藏

    感性认识 界面介绍,viewer Geocoder : 查找位置工具,查找到之后会将镜头对准找到的地址,默认使用bing地图 Home Button :视角返回初始位置. Scene Mode Pic ...