上一篇介紹了中間件,是給本篇做鋪墊用的,可以幫助你理解本篇介紹的異步Action洲拇。前幾篇所有的Action都是同步Action,即本地?cái)?shù)據(jù)塞進(jìn)Action中立即dispatch出去更新state曲尸。但現(xiàn)實(shí)中很多數(shù)據(jù)是需要從服務(wù)器端取的(常見ajax方式取數(shù)據(jù))赋续,因此需要異步Action。本篇的源碼已上傳Github另患,請(qǐng)參照src/reactReduxAsync文件夾纽乱。
其實(shí)Action是個(gè)plan object,不存在同步或者異步的概念昆箕。所謂的異步Action鸦列,本質(zhì)上是一系列Action動(dòng)作:
第一步:先dispatch出請(qǐng)求服務(wù)器數(shù)據(jù)的Action(通常此時(shí)state里會(huì)設(shè)計(jì)個(gè)loading或fetching的值,讓頁面呈現(xiàn)出loading狀態(tài))
第二步:服務(wù)器返回了數(shù)據(jù)(也可返回異常)为严,將數(shù)據(jù)塞入Action里编丘,再dispatch出這個(gè)Action去更新state。
第一步好實(shí)現(xiàn)挽拔,正常dispatch一個(gè)type為request的Action就行了吼拥。第二步也好實(shí)現(xiàn),正常dispatch一個(gè)帶服務(wù)器端數(shù)據(jù)的Action就行了夕吻。關(guān)鍵是如何將第一步和第二步捆綁起來诲锹,執(zhí)行第一步后,進(jìn)入等待狀態(tài)涉馅,自動(dòng)執(zhí)行第二步归园。這也是異步Action的關(guān)鍵,即redux-thunk中間件稚矿。
上一篇介紹過中間件:在Redux里中間件等同于修改Store.dispatch方法庸诱,將其變成洋蔥圈式的強(qiáng)化版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);
};
};
};
}
閱讀源碼可知桥爽,常規(guī)的Action creator只能返回一個(gè)Action,但有了redux-thunk昧识,你的Action creator還可以返回一個(gè)function(dispatch, getState)钠四。函數(shù)的參數(shù)一看名字就知道是干什么的,不贅述跪楞。目的就是將上述第一步發(fā)送request的Action和第二步發(fā)送取得數(shù)據(jù)后的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是個(gè)常規(guī)的發(fā)送request請(qǐng)求的Action creator,供第一步用缕碎。receiveData是個(gè)常規(guī)的攜帶數(shù)據(jù)的Action creator褥影,供第二步用。重點(diǎn)在如何將第一步和第二步打包進(jìn)fetchDataAction里阎曹。
fetchDataAction返回的是react-thunk支持的function(dispatch, getState)伪阶,而不是一個(gè)對(duì)象形式的Action。doFetchData里dispatch第一步的request請(qǐng)求的Action处嫌,(中間因?yàn)閿?shù)據(jù)取太快看不出效果栅贴,所以強(qiáng)制讓取數(shù)據(jù)延遲1秒,sleep(1000)這行代碼請(qǐng)無視)熏迹,然后向服務(wù)器fetch數(shù)據(jù)檐薯,取到數(shù)據(jù)后dispatch第二步的攜帶數(shù)據(jù)的Action。
中間插著一個(gè)canFetchData方法是為優(yōu)化用的注暗,當(dāng)正在請(qǐng)求數(shù)據(jù)時(shí)禁止重復(fù)請(qǐng)求數(shù)據(jù)坛缕,防止用戶狂點(diǎn)查詢按鈕,節(jié)省服務(wù)器開銷捆昏,這與本篇內(nèi)容無關(guān)赚楚,可以無視。
代碼雖短骗卜,但里面信息量卻不少宠页,你需要具備中間件, Promise寇仓,thunk的知識(shí)举户,ES6的基本語法知識(shí)也不可少。
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里只是單純處理數(shù)據(jù)遍烦,沒什么特別的俭嘁。需要注意的是,設(shè)計(jì)了一個(gè)fetching變量服猪,當(dāng)收到第一步request的Action時(shí)供填,將其設(shè)為true,觸發(fā)頁面的loading組件罢猪。當(dāng)收到第二步更新值的Action時(shí)捕虽,將其設(shè)為false,隱藏頁面的loading組件坡脐。
效果如下圖,點(diǎn)擊按鈕后獲取到數(shù)據(jù)房揭,你可以跟著教程自己嘗試一下:
至此Redux教程已經(jīng)結(jié)束备闲,順便把項(xiàng)目的目錄結(jié)構(gòu)也定了:
最后演痒,如果覺得教程還行,請(qǐng)不吝嗇Github上star一下趋惨,點(diǎn)這里鸟顺,這個(gè)要求不過分吧 _