はじめに
「Reduxはボイラープレートが多くて面倒」「action typeの定数、action creator、reducerと 同じような処理を3箇所に書かされる」——そんなイメージを持っている方は多いのではないでしょうか。 実際、初期のReduxはその通りで、単純なカウンターを作るだけでも何十行ものコードが必要でした。
しかし現在のReduxは、公式が「Redux Toolkit(RTK)」という標準ツールキットを前面に押し出しており、 かつての「面倒なReduxの姿」とはかなり違うものになっています。この記事では、 昔ながらのReduxの姿にも触れつつ、今のReduxがどれだけシンプルに書けるのかを実例で見ていきます。
Reduxとは
Reduxは、アプリケーション全体のstateを1つのstoreに集約し、 「stateは常にactionをディスパッチすることでしか変更できない」という 一方向のデータフローで管理するJavaScriptライブラリです。 React専用ではなく、Vueや素のJavaScriptからも利用できますが、 実際にはReactとの組み合わせ(React-Redux)で使われることがほとんどです。
主な特徴
- 予測可能なstate管理 - state変更は必ずactionという「何が起きたか」を表すオブジェクトを経由するため、変更履歴を追いやすい
- 単一のstore - アプリ全体のstateが1箇所に集約され、どのコンポーネントからも同じデータを参照できる
- Redux Toolkitによる簡潔さ - 公式が推奨する
createSliceやconfigureStoreを使えば、従来必要だったボイラープレートの大半が不要になる - 強力な開発者ツール - Redux DevToolsによって、actionの履歴やstateの変化をタイムトラベルしながらデバッグできる
インストール
React環境で使う場合は、Redux本体とReact-Redux、そしてRedux Toolkitを合わせて導入するのが現在の標準です。
npm install @reduxjs/toolkit react-redux
yarn add @reduxjs/toolkit react-redux
pnpm add @reduxjs/toolkit react-redux
基本的な使い方
まずはReact抜きで、Redux単体の最小構成を見てみましょう。createStoreでstoreを作り、
dispatchでactionを送り、subscribeで変化を監視するという、Reduxの根幹となる流れです。
store.dispatchに渡しているのがactionです。reducerは現在のstateとactionを受け取り、
「新しいstate」を返す純粋関数として書きます。このシンプルな仕組みが、
複雑なアプリでも予測可能なstate管理を実現している土台です。
実践的なユースケース
Redux Toolkitのslice機能で書く
素のReduxでは、action type定数・action creator・reducerを個別に書く必要がありましたが、
Redux ToolkitのcreateSliceを使えばこれらを1つにまとめられます。
さらにreducer内では、内部的にImmerが使われているため「stateを直接書き換える」ような
記述をしても、実際にはイミュータブルな更新が行われます。
state.count += 1のように直接代入しているにもかかわらず、実際にはイミュータブルな
新しいstateが生成されています。action creatorもcounterSlice.actionsから
自動で生成されるため、手書きするコード量が大幅に減っています。
Reactコンポーネントと連携する
実際のアプリでは、React-Reduxが提供するuseSelectorとuseDispatchフックを使って
コンポーネントからstoreにアクセスします。Providerでアプリ全体をラップすることで、
どの階層のコンポーネントからでもstoreを参照できるようになります。
useSelectorはstoreの中から必要な部分だけを取り出し、その値が変わったコンポーネントだけを
再レンダリングします。useDispatchで取得したdispatch関数にactionを渡すだけで、
storeの更新とUIの再描画が自動的につながります。
非同期処理をcreateAsyncThunkで扱う
API通信のような非同期処理は、Redux単体だとミドルウェアの追加設定が必要でしたが、
Redux ToolkitのcreateAsyncThunkを使えば、非同期処理の「開始・成功・失敗」という
3つの状態を自動的にactionとして扱えます。
fetchUser.pending・fetchUser.fulfilledといったactionが自動生成されるため、
ローディング状態の管理を自前で書く必要がありません。extraReducersでそれぞれの
タイミングに対応するstate更新を書くだけで、非同期処理の一連の流れを表現できます。
まとめ
Reduxは「ボイラープレートが多くて面倒」という評判が先行しがちですが、
Redux Toolkitを使えばcreateSliceによるaction・reducerの一元管理、
Immerによる直感的なstate更新、createAsyncThunkによる非同期処理の簡素化など、
かつての面倒な部分の多くが解消されています。単一のstoreで一方向のデータフローを保証する
Reduxの設計思想は、大規模なアプリほど威力を発揮します。まずは@reduxjs/toolkitから
触ってみて、そのシンプルさを体感してみてください。