SPA を作っていると複数の API に同時に接続して、通信を待って何かをする、というケースが頻繁にあります。そういった場合、Redux ではどうするのか考えてみました。

こういった場合、ミドルウェアには redux-saga を使うのが定石かもしれませんが、あの独特の記法がどうも好きになれないのと、処理が分散してコードが追いにくくて苦手なので、今回は redux-thunk の利用で検討してみます。

サンプルのソースはこちら。
https://github.com/ANTON072/redux-multiple-ajax-sample

デモ
https://anton072.github.io/redux-multiple-ajax-sample/

View と Reducer は特に変わったことはやっていません。
https://github.com/ANTON072/redux-multiple-ajax-sample/blob/master/src/App.js

https://github.com/ANTON072/redux-multiple-ajax-sample/blob/master/src/index.js#L18

const github = (state = initialState, action) => {
  switch (action.type) {
    case ActionTypes.FETCH_EXAMPLE_REQUEST:
      return { ...state, loading: true };
    case ActionTypes.FETCH_EXAMPLE_FAILURE:
      return { ...state, loading: false };
    case ActionTypes.FETCH_EXAMPLE_SUCCESS:
      return { ...state, ...action.payload, loading: false };
    default:
      return state;
  }
};

工夫のポイントは Action Creator です。

まず Action ですが、通信まわりは REQUEST / FAILURE / SUCCESS とステータスに応じて suffix を付けておきます。今回のネタには直接関係ないですが、suffix に定型句を付けておくと、ミドルウェアでの加工も簡単にできるので何かと便利です。視認性もいい。

export const FETCH_EXAMPLE_REQUEST = 'FETCH_EXAMPLE_REQUEST';
export const FETCH_EXAMPLE_FAILURE = 'FETCH_EXAMPLE_FAILURE';
export const FETCH_EXAMPLE_SUCCESS = 'FETCH_EXAMPLE_SUCCESS';

const AJAX_A_REQUEST = 'AJAX_A_REQUEST';
const AJAX_A_FAILURE = 'AJAX_A_FAILURE';
const AJAX_A_SUCCESS = 'AJAX_A_SUCCESS';

const AJAX_B_REQUEST = 'AJAX_B_REQUEST';
const AJAX_B_FAILURE = 'AJAX_B_FAILURE';
const AJAX_B_SUCCESS = 'AJAX_B_SUCCESS';

各 API を叩く Action Creator を定義します。redux-thunk を使っているので、Promise で返すことができます。

export function ajaxA() {
  return function(dispatch) {
    dispatch({ type: AJAX_A_REQUEST });
    return fetch('https://api.github.com/')
      .then(response => response.json())
      .then(
        json => dispatch({ type: AJAX_A_SUCCESS, payload: json }),
        err => dispatch({ type: AJAX_A_FAILURE, payload: err })
      );
  };
}

export function ajaxB() {
  return function(dispatch) {
    dispatch({ type: AJAX_B_REQUEST });
    return fetch('https://api.github.com/users/anton072')
      .then(response => response.json())
      .then(
        json => dispatch({ type: AJAX_B_SUCCESS, payload: json }),
        err => dispatch({ type: AJAX_B_FAILURE, payload: err })
      );
  };
}

ajaxA と ajaxB をバンドルする Action Creator を定義します。

export function exampleAjax() {
  return function(dispatch) {
    dispatch({ type: FETCH_EXAMPLE_REQUEST });
    return Promise.all([dispatch(ajaxA()), dispatch(ajaxB())])
      .then(responces => {
        dispatch({
          type: FETCH_EXAMPLE_SUCCESS,
          payload: {
            apis: responces[0].payload,
            user: responces[1].payload
          }
        });
      })
      .catch(err => {
        dispatch({
          type: FETCH_EXAMPLE_FAILURE,
          payload: err
        });
      });
  };
}

View からはこのバンドルされた関数を実行すれば、複数の非同期通信をまとめて実行することができます。

async / await を使うともうちょいオシャレな書き方もできるんですかね。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.