Posted at

ReduxでAPIの通信の状態を共通で扱うTips(feat. typescript-fsa)

APIコールが発生する処理を行った時にローディング中かどうかを示す loading なり fetching なりのフラグを用意すること、結構あると思います。これを様々なActionごとに逐一準備するのだるいのでまとめて用意する方法と、それをtypescript-fsa使うと意識すること一つ減るよという小ネタです。

考え方はこちらのブログのアイデア完全にお借りしていて、typescript-fsa前提だとAction Typeが必ず特定のフォーマットになるので幸せになったという話。

https://medium.com/stashaway-engineering/react-redux-tips-better-way-to-handle-loading-flags-in-your-reducers-afda42a804c6


typescript-fsaの非同期アクション

typescript-fsaでは非同期なAction用に便利ActionCreatorCreatorを用意してくれており、それがactionCreator.asyncです。

export const fetchSomethingActions = actionCreator.async<

{ fuga: string }, // parameter type
{ hoge: string }, // success type
{ code: number } // error type
>('FETCH_SOMETHING');

こうすると下記のような関数を実行できて、特定のフォーマットのAction Typeを持ったActionを発行してくれる。

// リクエスト実行時に呼び出す

// Action Typeは FETCH_SOMETHING_STARTED になる
dispatch(fetchSomethingActions.started({ params: { fuga: 'hoge' } }));

// リクエスト成功時に呼び出す
// Action Typeは FETCH_SOMETHING_DONE になる
dispatch(fetchSomethingActions.done({ params: { fuga: 'hoge' }, result: { hoge: 'fuga' } }));

// リクエスト失敗時に呼び出す
// Action Typeは FETCH_SOMETHING_FAILED になる
dispatch(fetchSomethingActions.failed({ params: { fuga: 'hoge' }, error: { code: 1 } }));

詳しくはREADMEで。

https://github.com/aikoven/typescript-fsa


APIの通信の状態専用のreducerを用意する

こんな感じのreducerを用意します。

export const loadingReducer = (state = {}, action: { type: string }) => {

const { type } = action;
const matches = /(.*)_(STARTED|DONE|FAILED)/.exec(type);

if (!matches) {
return state;
}

const [, requestName, requestState] = matches;
return {
...state,
[requestName]: requestState === 'STARTED'
};
};

正規表現で STARTED|DONE|FAILED いずれかが後ろに付いているかを判定しています。もしついている場合、STARTED の場合はまさにリクエストが走った時なのでtrueにします。成功か失敗した時はfalseにします。

あとはmapStateのとこでリクエスト名をキーに取ってくるだけ。

当たり前なんですがAction Typeのフォーマットを整えるのはtypescript-fsaの特権ではなく、普通に規約で縛ればいい話なんですが、typescript-fsaのようなActionCreatorCreatorがある前提なら意識しなくても守れるのが結構気分良かったです。