My External Storage

Oct 10, 2018 - 4 minute read - Comments - react redux

redux-actionsのcreateActionでFlux Standard Actionに則ったerror=trueなActionを作る #react

redux-actionsを使うと、Flux Standard Action(FSA)に則ったActionを簡単に作れる。
が、エラーがあったときのAtionはどう作ればよいのだろう。
よく読めば公式に書いてあるのだが、直接的なコードはなかったのでメモ。

TL;DR

// 通常は{type="READ_EVENTS", payload=response.data}なActionが生成される。
// 例外発生時は{type="READ_EVENTS", payload=Error, error=true}なActionが生成される。
export const readEvents = createAction("READ_EVENTS", () =>
  axios.get(`${RESOURCE_URL}`).then(response => {
    return response.data;
  })
);

動作する文中のサンプルコードのプロジェクトは以下になる。

利用するredux-actionsのバージョンは2.6.1。各ライブラリのバージョンの詳細は以下のファイルを参照のこと。

FSA準拠のアクションを作る

Flux Standard ActionはReact + Redux(Flux)で利用するActionの形を定めている。

Flowtypeの記法で型を書くと、Actionは以下のような型になる。

export type ActionT<A: Action, P> = {|
  type: A,
  payload?: P | Error,
  error?: boolean,
  meta?: mixed
|};
プロパティ名 意味
type Actionを識別する一意な文字列
payload ロジックの結果、もしくはError。Optional
error 処理が失敗した場合trueを返す真偽値。Optional
meta ロジックの結果には無関係なデータ。Optional

redux-actionsのcreateActionでActionを作る

Redux利用時はredux-actionsのcreateAction(type, payloadCreator)を使うと、このActionを簡単に作れる。
第一引数にtypeに相当する情報、第二引数にはpayloadに入れる情報を返す関数を渡す(引数が2つでないcreateActionもある)。
redux-promiseなどと組み合わせると非同期処理も含めることができる。

import axios from 'axios';
import { createAction } from 'redux-actions';

export type ReadEventsAction = {
  type: typeof ReadEvents,
  payload?: EventMap | Error,
  error?: boolean
};

// 通常は{type="READ_EVENTS", payload=response.data}なActionが生成される。
// 例外発生時は{type="READ_EVENTS", payload=Error, error=true}なActionが生成される。
export const readEvents: void => ReadEventsAction = createAction("READ_EVENTS", () =>
  axios.get(`${RESOURCE_URL}`).then(response => {
    return response.data;
  })
);

createActionで{type=FOO, payload=Error, error=true}なオブジェクトを返す

上記のサンプルコードのコメントに書いたが、内部でエラーが発生すれば自動的にActionのなかのerrortrueになる。
reducerの中で使えるhandleAction関数も用意されているので、error = trueのときの処理をthrowに書けばよい。

import { handleActions, type ActionType } from 'redux-actions';

const initialState: {
  events: EventMap | {}
} = { events: {} };

export default handleActions(
  {
    ["READ_EVENTS"]: {
      // Actionが正常に完了したときの処理
      next(state: { events: EventMap | {} }, action: ActionType<typeof readEvents>) {
        console.log(action);
        return { ...state, events: action.payload };
      },
      // Actionが異常終了したときの処理
      throw(state: { events: EventMap | {} }, action: ActionType<typeof readEvents>) {
        // エラー処理など
        return { ...state, events: {} };
      }
    }
  },
  initialState
);

最後に

redux-actionsを使えばいい感じに処理してくれる"のを期待して使ってみたが、ここまでもろもろ処理してくれると思っていなかった。
ずっと「createActionpayloadの値は操作できるけど、errortrueにするにはどうするんだろう?」とハマっていた。
ちゃんとドキュメントを読んでいなかったのが原因。とは言え、以下のように書いてあってもなんかピンと来ない気がする。

NOTE: If payload is an instance of an Error object, payloadCreator will not be called.

payloadCreatorを呼ばないとpayloadインスタンスはわからないと思うのだが…createAction(type)の説明から続いているのだろうか。

If the payload is an instance of an Error object, redux-actions will automatically set action.error to true.

こっちの一文はわかりやすい。

参考

関連記事