React での開発において、コンポーネントをまたいだ状態管理は一つの大きなテーマです。「Redux」はその代表的な解決策ですが、従来の方法は記述量が多く、複雑な設定(いわゆるボイラープレート)が必要で、挫折してしまった方も多いのではないでしょうか。

そんな悩みを解決するために登場したのが Redux Toolkit (RTK) です。Redux をよりシンプルに、そして効率的に使えるように設計されており、現在では Redux の公式推奨ライブラリとなっています。

この記事では、Redux Toolkit を使った状態管理の基本的な流れを、できるだけシンプルなカウンターアプリを例に解説します。

Redux Toolkit の導入

まずは、プロジェクトに必要なパッケージをインストールします。React プロジェクトのルートディレクトリで、以下のコマンドを実行してください。

@reduxjs/toolkitが Redux Toolkit 本体、react-reduxが React と Redux を連携させるためのライブラリです。

npm の場合

npm install @reduxjs/toolkit react-redux

yarn の場合

yarn add @reduxjs/toolkit react-redux

ストアの作成

次に、アプリケーション全体の状態を保持する「ストア」を作成します。Redux Toolkit ではconfigureStoreという関数を使うことで、非常に簡単にストアをセットアップできます。

srcディレクトリにappフォルダとfeaturesフォルダを作成しましょう。このように機能をfeaturesに、ストア設定をappに分けるのは、公式で推奨されている構成です。

src/app/store.js

import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

configureStoreは、Redux DevTools 拡張機能との連携などを自動で行ってくれるため、これだけで基本的なストアの設定は完了です。reducerオブジェクトの中に、後述する「Slice」からエクスポートした Reducer を登録していきます。

Redux Toolkit の Slice(スライス)の作成

Slice(スライス)は、Redux Toolkit の核心的な機能です。アプリケーションの状態を機能ごとに分割して管理するための仕組みで、従来バラバラに記述していたActions、Action Types、Reducers のすべてを一つのファイルにまとめることができます。

srcディレクトリのfeatures/counterフォルダ内にcounterSlice.jsを設置します。

src/features/counter/counterSlice.js

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  value: 0,
};

export const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    increment: (state) => {
      // Redux ToolkitはImmerライブラリを使用しているため、
      // stateを直接変更するようなロジックを記述できる
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

// 各コンポーネントで使えるようにActionをエクスポート
export const { increment, decrement } = counterSlice.actions;

// storeで使えるようにreducerをエクスポート
export default counterSlice.reducer;

createSliceは、name(スライスの名前)、initialState(初期状態)、reducers(状態を更新するロジック)の 3 つを設定します。

注目すべきは、state.value += 1のように、まるで状態を直接変更しているかのように書ける点です。これは内部で Immer というライブラリが働いており、実際には安全なイミュータブル(不変的)な更新が行われるため、安心して記述できます。

React-Redux の Provider 設定

作成したストアを React アプリケーション全体で利用できるように、ProviderコンポーネントでアプリケーションのルートとなるAppコンポーネントをラップします。

src/main.jsx (または src/index.js)

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { store } from "./app/store";
import { Provider } from "react-redux";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

src/App.jsx

import React from "react";
import { Counter } from "./features/counter/Counter";
import "./App.css"; // スタイルはお好みで

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Counter />
      </header>
    </div>
  );
}

export default App;

このようにAppコンポーネントを挟むことで、より実際のアプリケーションに近い構造になります。これで、App以下のすべての子コンポーネントから Redux ストアにアクセスできるようになりました。

Redux Hooks の実装

最後に、React コンポーネントからストアの値を参照したり、値を更新するための Action を呼び出したりします。これにはuseSelectoruseDispatchという 2 つのフックを使用します。

src/features/counter/Counter.jsx

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { decrement, increment } from "./counterSlice";

export function Counter() {
  // ストアの状態からcounter.valueの値を取得
  const count = useSelector((state) => state.counter.value);

  // Actionを呼び出すためのdispatch関数を取得
  const dispatch = useDispatch();

  return (
    <div>
      <div>
        <button aria-label="Increment value" onClick={() => dispatch(increment())}>
          +
        </button>
        <span>{count}</span>
        <button aria-label="Decrement value" onClick={() => dispatch(decrement())}>
          -
        </button>
      </div>
    </div>
  );
}
  • useSelector: ストアの状態(state)を引数に取り、そこから必要な値を抽出するためのフックです。
  • useDispatch: Action をdispatch(発行)するための関数を返します。

💡 パフォーマンスのヒント

useSelectorは、返した値が変更されるたびにコンポーネントを再レンダリングします。不要な再レンダリングを防ぐため、state.counterのようにオブジェクト全体を返すのではなく、 state.counter.valueのようにコンポーネントで使う最小限の値だけを返す ように心がけましょう。

まとめ

Redux Toolkit を使うことで、かつての Redux が抱えていた多くの課題が解決されていることがお分かりいただけたかと思います。

  • configureStore でストアのセットアップが劇的に簡素化
  • createSlice で Actions、Reducers、Constants が一つにまとまり、記述量が大幅に削減
  • Immerの統合により、安全性を保ちつつ直感的なロジックの記述が可能に

Redux Toolkit は、もはや Redux を使う上での「選択肢」ではなく「スタンダード」です。ぜひこの機会にマスターして、快適な React 開発に役立ててください。🚀