说明: 对 Redux 不了解的同学可先看看这篇文章 Redux 技术架构简介 (一)
前言
这里说的 Redux 异步实现, 是专指 Redux 中的异步 Action 实现, 而不是指应用的异步实现, 因为 Redux 本身只支持同步 action, 即发送 action,state 立即更新; 那如果我在发送一个 action 后, 需要 state 过一段时间再更新呢? 按正常的思路 redux 是无法处理这种情况的, 下面就来看看异步 Action 是如何实现的吧!
需要提的一点是, 其实完全可以将异步逻辑写在 View 中, 然后在回调函数中发送 action. 但是如果你要配合 react 一起使用, 这样做就违背了 react-redux 的设计思想, 即 UI 与逻辑的分离 (具体的实现可以在下一篇文章中看到), 而且当存在多个异步请求时也很难将异步逻辑抽象出来, 所以异步逻辑应该由 Redux 架构实现, 这样也就必须实现发送异步 action 了
1. 中间件 (Middleware)
为了解决上面提到的问题, 我们需要引入中间件的概念.
(1) 时机
中间件执行的时机是在 action 发起之后, reducer 执行之前.
即在 dispatch 一个 action 之后, 经过一系列的中间件处理过程, 再进行 reducer.
(2) 原理
本质上, 中间件就是对 dispatch 函数的改造. 当执行 dispatch(action) 时, 会先调用中间件, 进行一些内部逻辑处理, 如: 添加日志等, 之后再执行 dispatch. 如果要支持中间件的链式调用, 必须再返回一个 dispatch.
下面是中间件的简单实现:
- function logger(store) {
- // 这里的 next 必须指向前一个 middleware 返回的函数:
- const next = store.dispatch
- return function dispatchAndLog(action) {
- console.log('dispatching', action)
- let result = next(action)
- console.log('next state', store.getState())
- return result
- }
- }
(3) 在 Redux 中应用中间件
可以使用 Redux 的 API-applyMiddleware 直接使用中间件, 代码如下:
- import {applyMiddleware,createStore} from 'redux';
- import {createLogger} from 'redux-logger';//log 中间件
- import thunk from 'redux-thunk';// 将 dispatch 改造成可以接受函数作为参数的中间件
- import indexPhotomainReducer from '../reducer/indexPhotomainReducer';
- const logger = createLogger();
- const store = createStore(
- indexPhotomainReducer,
- applyMiddleware(thunk, logger)
- );
由此可见, 可以把 applyMiddleware 作为 createStore 的第二个参数传入, 你所使用的中间件需要下载单独 npm 包, 然后按顺序传入 applyMiddleware 函数中 (注: 中间件有顺序要求, 需要看下每个中间件的使用文档, logger 一般要放在最后).
2. Redux 异步实现
(1) Action 设计
需要增加三种 action
通知异步请求发起的 action
异步请求成功的 action
异步请求失败的 action
示例代码如下:
- export const requestPostsAction = () => {
- return {
- type: REQUEST_POSTS
- };
- }
- export const receivePostsSuccessAction = (data) => {
- return {
- type: RECEIVE_POSTS_SUCCESS,
- data
- };
- }
- export const receivePostsFailureAction = (error) => {
- return {
- type: RECEIVE_POSTS_FAILURE,
- error
- };
- }
返回参数完全可以自定义. 这 3 种 action 分别在请求开始前, 请求成功后, 请求失败后发送.
(2) State 设计
为了配合异步 Action, 可以在 state 树中增加 2 个属性:
isFetching: 表示是否正在处理异步请求.
isValidate: 表示数据的有效性, 他的作用是在异步请求发送失败后, 告诉 View 当前 state 的数据是过时的数据.
State 属性可以凭自己喜好随意设计. 设计好后可以这样编写 reducer:
- let sliderReducer = function (state = initialState, action) {
- switch(action.type){
- case photomainAction.RECEIVE_POSTS_SUCCESS:
- return Object.assign({}, state, {photoData,videoData,isFetching:false,isValidate:true});
- case photomainAction.RECEIVE_POSTS_FAILURE:
- return Object.assign({}, state, {isFetching:false,isValidate:false});
- case photomainAction.REQUEST_POSTS:
- return Object.assign({}, state, {isFetching:true,isValidate:false});
- default:
- return state;
- }
- }
(3) redux-thunk 中间件
当设计好 action 和 state 后, 我们想要达到的效果是 -- 可以像同步一样 dispatch 一个 action, 而不用考虑异步逻辑.
为了达到这种效果, 首先面临的问题是, 异步逻辑放在哪里? 这里只有 2 个选择: action 中或 reducer 中 (store 是不适合处理数据的). 由于 reducer 必须是一个纯函数, 他不适合处理像异步请求这样存在不确定的输出的逻辑. 最后只能放在 action 中处理了.
这时, 为了处理异步请求, action 创建函数需要返回一个带有异步请求逻辑的函数, 而不是一个对象了. 而 dispatch 只能接受对象, 不能接受函数作为参数, 这样就面临又一个问题: 如何让 dispatch 接受函数?
接下来就是 redux-thunk 中间件发挥作用的时候了, 他可以让 dispatch 接受一个函数 (原理就是上一节讲的, 他其实是改写了 dispatch 函数), 最终异步的 action 可以这样实现:
- // 定义一个 action creator - fetchPosts
- export const fetchPosts = () => (dispatch, getState) => {
- dispatch(requestPostsAction());
- return window.fetch("/photo/initPage").then(response=>{
- if(response.ok){
- return response.json();
- }else{
- dispatch(receivePostsFailureAction("error"));
- }
- }).then(data => {
- if(data){
- dispatch(receivePostsSuccessAction(data));
- }else{
- dispatch(receivePostsFailureAction("error"));
- }
- });
- }
这样就可以像同步一样发送 action 了, 即:
dispatch(fetchPosts());
接下来只需静静等待 view 的更新就行了, 这样就实现了整个 Redux 的异步流程.
参考
Redux 中文文档 http://cn.redux.js.org/
Redux 入门教程 - 阮一峰 http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
来源: https://juejin.im/post/5afb9637f265da0b9c10cd69