[譯]在 React & Redux 中使用 AJAX 輪詢

劉嘉一發表於2019-03-03

更新: 檢視最新關於使用 redux-saga 進行輪詢的文章:notjoshmiller.com/ajax-pollin…

正如生活不總是給予你所需之物,你所用的 API 也不總是支援流式事件。因此,當你需要把一些有時序依賴的狀態從服務端同步到客戶端時,一個常用的 “曲線救國” 方法就是使用 AJAX 進行介面輪詢。我們大部分人都知道使用 setInterval 並不是處理輪詢的 “最佳人選”,不過它的堂兄 setTimeout 配合 遞迴解法 卻可以大展身手。

React & Redux 為我們提供了響應式的資料流,我們如何才能使普通的輪詢方法與其和諧共處?RxJS 以及其他 Observable 類庫是處理輪詢的不錯選擇,不過除非你的專案已經整合了 Observable 類庫,否則僅為解決輪詢而引入相關類庫顯得並不值當。當前通過結合 React 元件的生命週期方法和 Redux 的 Action 就已經足夠處理 AJAX 輪詢,下面來看看如何得解?

首先通過 Redux 的 Reducer 來說明當前 State:

const initialState = {  
    data: {},
    isFetching: false
};

export function data (state = initialState, action) {  
    switch (action.type) {
    case DATA_FETCH_BEGIN: {
        return { ...state, isFetching: true };
    }
    case DATA_FETCH_SUCCESS: {
        return { isFetching: false, data: { ...state.data, action.payload }};
    }
    case DATA_FETCH_ERROR: {
        return { ...state, isFetching: false };
    }
    default:
        return state;
}
複製程式碼

我不會在這裡去講解如何處理 Redux 中的非同步 Action 建立函式,想更好地瞭解這方面知識請參考 Redux 文件中的非同步示例。 現在只需假設我們已有相關的 Redux 中介軟體來處理本文提到的各種 Action 。我會使用與 real-world example(譯註:原文連結的倉庫已不存在,可以參考 Redux 文件中同名例子)中相似形式的 Action 建立函式。

對應上方的資料模型,我們的 Action 建立函式可能為:

export function dataFetch() {  
  return {
    [CALL_API]: {
      types: [DATA_FETCH_BEGIN, DATA_FETCH_SUCCESS, DATA_FETCH_ERROR],
      endpoint: `api/data/`
    }
  };
}
複製程式碼

回到最初的問題,讓我們想想你會如何實現 API 介面的輪詢。你會把輪詢的定時器設定在 Reducer 中?還是 Action 建立函式裡?或許是中介軟體裡?如果把定時器放到 Smart 元件(譯註:參看 Smart and Dumb Components – Medium)中怎麼樣呢?我會選擇在元件中設定定時器,不僅是因為元件需要控制自身的資料依賴,而且我們可以通過元件的生命週期方法控制這些定時器,看看如何做到?

import React from `react`;  
import {connect} from `react-redux`;  
import {bindActionCreators} from `redux`;  
import * as DataActions from `actions/DataActions`;

// 元件需要哪些 Redux 全域性狀態作為 props 傳入?
function mapStateToProps(state) {  
    return {
        data: state.data.data,
        isFetching: state.data.isFetching
    };
}

// 元件需要哪些 Action 建立函式作為 props 傳入?
function mapDispatchToProps(dispatch) {  
    return {
        dataActions: bindActionCreators(DataActions, dispatch)
    };
}

@connect(mapStateToProps, mapDispatchToProps)
export default class AppContainer {  
    componentWillReceiveProps(nextProps) {
        if (this.props.data !== nextProps.data) {

            clearTimeout(this.timeout);

            // 你可以在這裡處理獲取到的資料

            if (!nextProps.isFetching) {
                this.startPoll();
            }
        }

    }

    componentWillMount() {
        this.props.dataActions.dataFetch();
    }

    componentWillUnmount() {
        clearTimeout(this.timeout);
    }

    startPoll() {
        this.timeout = setTimeout(() => this.props.dataActions.dataFetch(), 15000);
    }
}
複製程式碼

好了,大功告成。因為上面的元件需要一些額外資料進行渲染,所以它會在掛載的時候嘗試獲取這些資料。 當 dataFetch 傳送了一個新 Action 後,我們的 Reducer 會返回新的狀態, 進而觸發元件的 componentWillReceiveProps 方法。在這個生命週期方法內會首先清除所有進行中的定時器,若當前沒有進行資料請求則隨即啟動一個新定時器。

誠然還有很多方法可以處理這裡的介面輪詢問題,並且如果有任何長輪詢方法可用時,此處的輪詢方法便相形見絀。不過我還是希望這篇文章可以幫助闡明結合 React 生命週期方法和 Redux 資料流的處 “事” 之道。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章