Redux介绍之异步Action

上一篇介绍了中间件,是给本篇做铺垫用的,可以帮助你理解本篇介绍的异步Action。前几篇所有的Action都是同步Action,即本地数据塞进Action中立即dispatch出去更新state。但现实中很多数据是需要从服务器端取的(常见ajax方式取数据),因此需要异步Action。本篇的源码已上传Github,请参照src/reactReduxAsync文件夹。

其实Action是个plan object,不存在同步或者异步的概念。所谓的异步Action,本质上是一系列Action动作:

第一步:先dispatch出请求服务器数据的Action(通常此时state里会设计个loading或fetching的值,让页面呈现出loading状态)

第二步:服务器返回了数据(也可返回异常),将数据塞入Action里,再dispatch出这个Action去更新state。

第一步好实现,正常dispatch一个type为request的Action就行了。第二步也好实现,正常dispatch一个带服务器端数据的Action就行了。关键是如何将第一步和第二步捆绑起来,执行第一步后,进入等待状态,自动执行第二步。这也是异步Action的关键,即redux-thunk中间件

上一篇介绍过中间件:在Redux里中间件等同于修改Store.dispatch方法,将其变成洋葱圈式的强化版Store.dispatch方法。redux-thunk中间件的源码总共15行,直接贴出来:

function createThunkMiddleware(extraArgument) {
  return function (_ref) {
    var dispatch = _ref.dispatch,
        getState = _ref.getState;
    return function (next) {
      return function (action) {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }

        return next(action);
      };
    };
  };
}

阅读源码可知,常规的Action creator只能返回一个Action,但有了redux-thunk,你的Action creator还可以返回一个function(dispatch, getState)。函数的参数一看名字就知道是干什么的,不赘述。目的就是将上述第一步发送request的Action和第二步发送取得数据后的Action封装在里面。

entries/reactReduxAsync.js:先在入口处引入redux-thunk

import thunk from 'redux-thunk';

...
const store = createStore(reducer, compose(
    applyMiddleware(thunk, logger),
    window.devToolsExtension ? window.devToolsExtension() : (f) => f,
));

actions/fetchData.js:

import fetch from 'isomorphic-fetch';
import * as constant from '../configs/action';
import { sleep } from '../lib/common';

const requestData = () => ({
    type: constant.REQUEST_DATA,
});

const receiveData = (data) => ({
    type: constant.RECEIVE_DATA,
    data: data.msg,
});

const doFetchData = () => async(dispatch) => {
    dispatch(requestData());
    await sleep(1000);      // Just 4 mock
    return fetch('./api/fetchSampleData.json')
        .then((response) => response.json())
        .then((json) => dispatch(receiveData(json)));
};

const canFetchData = (state) => {
    return !state.fetchData.fetching;
};

export default {
    fetchDataAction: () => (dispatch, getState) => {
        if (canFetchData(getState())) {
            return dispatch(doFetchData());
        }
        return Promise.resolve();
    },
};

解释一下,requestData是个常规的发送request请求的Action creator,供第一步用。receiveData是个常规的携带数据的Action creator,供第二步用。重点在如何将第一步和第二步打包进fetchDataAction里。

fetchDataAction返回的是react-thunk支持的function(dispatch, getState),而不是一个对象形式的Action。doFetchData里dispatch第一步的request请求的Action,(中间因为数据取太快看不出效果,所以强制让取数据延迟1秒,sleep(1000)这行代码请无视),然后向服务器fetch数据,取到数据后dispatch第二步的携带数据的Action。

中间插着一个canFetchData方法是为优化用的,当正在请求数据时禁止重复请求数据,防止用户狂点查询按钮,节省服务器开销,这与本篇内容无关,可以无视。

代码虽短,但里面信息量却不少,你需要具备中间件Promisethunk的知识,ES6的基本语法知识也不可少。

reducers/fetchData.js:

import * as constant from '../configs/action';
import { createReducer } from '../lib/common';

const initialState = {
    fetching: false,
    data: null,
};

export default createReducer(initialState, {
    [constant.REQUEST_DATA]: (state, action) => {
        return {
            ...state,
            fetching: true,
        };
    },
    [constant.RECEIVE_DATA]: (state, action) => {
        return {
            ...state,
            fetching: false,
            data: action.data,
        };
    },
});

Reducer里只是单纯处理数据,没什么特别的。需要注意的是,设计了一个fetching变量,当收到第一步request的Action时,将其设为true,触发页面的loading组件。当收到第二步更新值的Action时,将其设为false,隐藏页面的loading组件。

效果如下图,点击按钮后获取到数据,你可以跟着教程自己尝试一下:


至此Redux教程已经结束,顺便把项目的目录结构也定了:


你可以根据业务需要再加上apis(统一管理服务器请求),i18n(多国语目录),styles(通用的css样式),template目录(HTML模板。因为教程有多篇,所以我将template目录放到了项目的根目录下)。将目录结构,和通用方法加入你们项目的脚手架里,这样就可以规范react-redux项目的代码。

最后,如果觉得教程还行,请不吝啬Github上star一下,点这里,这个要求不过分吧 _

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容