企業級後臺react-redux異步操做實踐

1、redux基礎

redux

  1. 經過 dispatch(action) -> 中間件 -> reducer處理數據 -> 改變store -> 使用subscribe()監聽store改變動新視圖 的方式管理狀態
  2. 將全部狀態存儲在一個store對象裏面
  3. reducer爲純函數,而異步操做因爲結果的不肯定性因此含有反作用,因此須要特殊處理

react-redux

  1. 容器組件,負責管理數據和業務邏輯,不負責UI呈現
  2. UI組件,提供UI呈現,無狀態即不使用this.state,狀態所有由this.props提供
  3. 由connect生成容器組件,每次store改變會調用connect,connect接收兩個參數: mapStateToProps, mapDispatchToProps
  4. mapStateToProps,將狀態映射到UI組件的props
  5. mapDispatchToProps,將dispatch方法映射到UI組件的props
  6. Provider組件,使用content API將store從頂層開始傳到每一層component供connect使用

2、redux處理異步的中間件

redux-thunk

  1. redux-thunk中間件容許action是一個方法
  2. 中間件收到action後會執行action方法並將結果提供給reducer
  3. action混亂致使不易維護

redux-saga

  1. saga會監聽action並基於這個action執行Effects操做
  2. Effects提供靈活的API,包括阻塞、非阻塞調用,取消、等待、race等操做
  3. 方便隔離並執行異步操做,並易於測試

3、redux-request-async-middleware

先從redux文檔中的異步action提及,每一個接口調用須要dispatch三個同步action,分別是:

  1. 一種通知 reducer 請求開始的 action。對於這種 action,reducer 可能會切換一下 state 中的 isFetching 標記。以此來告訴 UI 來顯示加載界面。
  2. 一種通知 reducer 請求成功的 action。對於這種 action,reducer 可能會把接收到的新數據合併到 state 中,並重置 isFetching。UI 則會隱藏加載界面,並顯示接收到的數據。
  3. 一種通知 reducer 請求失敗的 action。對於這種 action,reducer 可能會重置 isFetching。另外,有些 reducer 會保存這些失敗信息,並在 UI 裏顯示出來。

也就是一個接口發起是這樣的javascript

dispatch(fetchPostsRequest(subject));
fetch(url).then(res => {
    dispatch(fetchPostsSuccess(subject, res));
}).catch(e => {
    dispatch(fetchPostsFailure(subject, e));
})

而我作的事情只是將這個操做封裝進中間件裏,特殊的地方在於:

  1. 全部的異步請求共用這三個action
  2. 用subject來區分是哪個請求
  3. 將全部的結果都放到store.requests裏

中間件源碼

export const reduxRequest = store => next => action => {
    let result = next(action);
    let { type, subject, model } = action;
    let _next = action.next;
    if(type === FETCH_POSTS_REQUEST) {
        model().then(response => {
            _next && _next(response);
            store.dispatch(fetchPostsSuccess(subject, response));
        }).catch(error => {
            console.error(error);
            store.dispatch(fetchPostsFailure(subject, error));
        });
    }
    return result
};
  1. 和redux-thunk同樣,將方法放進action裏
  2. 中間件攔截FETCH_POSTS_REQUEST action,並進行異步處理

reducer源碼

export const requests = (state = {}, action) => {
    switch (action.type) {
        case FETCH_POSTS_REQUEST:
            return assign({},
                state,
                {
                    [action.subject]: {
                        isFetching: true,
                        state: 'loading',
                        subject: action.subject,
                        response: null,
                        error: null,
                    }
                }
            );
        case FETCH_POSTS_FAILURE:
            return assign({},
                state,
                {
                    [action.subject]: {
                        isFetching: false,
                        state: 'error',
                        subject: action.subject,
                        response: state[action.subject].response,
                        error: action.error,
                    }
                }
            );
        case FETCH_POSTS_SUCCESS:
            return assign({},
                state,
                {
                    [action.subject]: {
                        isFetching: false,
                        state: 'success',
                        subject: action.subject,
                        response: action.response,
                    }
                }
            );
        case FETCH_POSTS_CLEAR:
            return assign({},
                state,
                {
                    [action.subject]: {
                        isFetching: false,
                        state: 'cleared',
                        subject: null,
                        response: null,
                        error: null,
                    }
                }
            );
        default:
            return state;
    }
}
  1. 將結果放入該subject對應下的response,若是錯誤的話將錯誤信息放入error當中
  2. isFetching表示當前的請求狀態
  3. 另外還加入了當前的狀態state和subject信息

將請求進行封裝

const request = (subject, model, next) => {
    _dispatch(fetchPostsRequest(subject, model, next));
    return true;
};
  1. 寫一個方法來發起FETCH_POSTS_REQUEST action
  2. 也就是說寫請求的時候不用再管action這東西了,直接調用request方法

將結果進行封裝

const getResponse = state =>
    state
    && state.response !== null
    && state.response;

const getLoading = (states = []) =>
    states.reduce((pre, cur) =>
        pre || (cur && cur.isFetching)
        , false)
    || false;
  1. 能夠獲取結果和多個請求下loading的狀態
  2. 有更多的操做或者格式還能夠繼續封裝,好比列表

使用方法redux-request-async-middleware

4、總結

  1. 使用了redux來進行狀態管理,而並不須要編寫redux那一套複雜邏輯,最大程度的減小異步操做的複雜度
  2. 適用於前端經過接口來處理和存儲數據的項目
  3. 接口由redux處理,而視圖組件由內部state來處理,而外部只暴露簡單的接口來進行操做,分離業務層和視圖層
  4. 對比react 16.3 new content API,redux的優點在於熱插播的中間件和純函數reducer寫法

更多文章 yjy5264.github.io

相關文章
相關標籤/搜索