說明:對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的非同步流程。
本篇到此告一段落,下一篇介紹React與Redux整合技術。