【Redux】ActionType, ActionCreator, Actions を書くときの個人的ベストプラクティス
TypeScriptでの話です。 不要論者はさっさと帰れ
バージョンは 2021/02/20 現在最新の 4.1.5 の話ではあるけど、 ReturnType
とか as const
が使える 3.4 以降が推奨。
ちなみにこの書き方は Redux には一切依存していないので、 useReducer を使うときに書く Action 周りでも使えるため、 useReducer 使ってたけどやっぱり Redux 使いたくなったときも移行が楽です。
ActionType
export const COUNT_UP = "COUNT_UP" as const;
ポイント
as const
とすることで ActionType の型がstring
に推論されてしまうのを防ぐ- と思ってたんですが、 TypeScript 4.1.5 現在だと
as const
しなくてもリテラル型に推論されるっぽいのでなくてもいいかも
- と思ってたんですが、 TypeScript 4.1.5 現在だと
- きちんと定数として定義する
ActionCreator
export const CountUp = (increases: number) => ({ type: COUNT_UP, payload: { increases } });
ポイント
- ActionType の定義の真下に書く
- 無駄に場所を離すと修正するときにダルいので近いところに定義する
function
は書くのがダルいのでアロー関数で書く- 関数名は Upper Camel のほうが見やすいのですき
dispatch(countUp(1))
よりdispatch(CountUp(1))
のほうがメリハリ付いて読みやすくない?程度なのでプロジェクトと好みに合わせて
payload
には変数名をキーにしてくれる構文(名前知らない)を使って簡潔に書く
Actions
export type Actions = ReturnType< | typeof CountUp | typeof OtherAction >;
ポイント
ReturnType
は1回、typeof
は毎回- なんか ActionCreator をオブジェクトで定義して Mapped Type で一気に生成する書き方も見つけたけど、Utiliy Typeを自分で書かずに組み込み型だけで行けるこれが一番シンプルだと思う
- 結局 reducer の switch-case で推論ができれば何でも良い
おまけ: Reducer の書き方
export function reducer(state: State, action: Actions): State { switch(action.type) { case COUNT_UP: { return { ...state, count: state.count + action.payload.increases }; } } return state; }
ポイント
- 戻り値の型をきちんと書く
- うっかり色々書き忘れて return する state の型が不完全になるのを防ぐため
- case節内は
{}
で囲う- 好みのレベルだけど、ここで変数を定義したくなったときに eslint の no-shadow に引っかかることがあるのでやっといて損はない。
{}
まみれになって嫌なら書かないし、気にならないなら書く、ぐらいでよいと思う
- 好みのレベルだけど、ここで変数を定義したくなったときに eslint の no-shadow に引っかかることがあるのでやっといて損はない。
- default節は書かない
- default節を書いてしまうと、caseで拾い損ねている Action があってもエラーにならなくなるため
まとめ
そろそろみんなで書き方統一しない??って思ったので書きました。
なにか意見あればブコメでもTwitterでもここのコメント欄でも積極的に書いていってください。