redux|redux action ajax,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,
【redux|redux action ajax,Redux介绍之异步Action】));
actions/fetchData.js:
import fetch from 'isomorphic-fetch';
import * as constant from '../configs/action';
import { sleep } from '../lib/common';
const requestData = https://www.it610.com/article/() => ({
type: constant.REQUEST_DATA,
});
const receiveData = https://www.it610.com/article/(data) => ({
type: constant.RECEIVE_DATA,
data: data.msg,
});
const doFetchData = https://www.it610.com/article/() => 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 = https://www.it610.com/article/(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方法是为优化用的,当正在请求数据时禁止重复请求数据,防止用户狂点查询按钮,节省服务器开销,这与本篇内容无关,可以无视。
代码虽短,但里面信息量却不少,你需要具备中间件, Promise,thunk的知识,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一下,点这里,这个要求不过分吧 _

    推荐阅读