Redux技術架構簡介(二)-- 非同步實現

brantni發表於2018-05-16

說明:對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執行之前。

Redux技術架構簡介(二)-- 非同步實現
即在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整合技術

參考

Redux 中文文件
Redux 入門教程-阮一峰

相關文章