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がある前提なら意識しなくても守れるのが結構気分良かったです。