Reduxでグローバルな状態を管理する【Redux Toolkit/カウンターアプリ】

JavaScript

Reduxは状態管理のライブラリです。今回Reduxを使ってカウンターアプリを作成しました。
基本的な使い方になるのでメモしておきます!

用語や基本的な使い方はこちらの記事にまとめました。

サンプル:カウンターアプリ(JavaScript)

今回作成するカウンターアプリです。練習で作成する定番のアプリですね!
プルダウンメニューで増減する値を1、5、10から選べます。増減値をグローバル値として管理します。

実際の動きはこちらで確認できます!

サンプル:カウンターアプリ(TypeScript)

TypeScript版も作ったので今後の参考としてにメモしておきます。動きは全く同じです。

TypeScriptの使い方は、公式サイトで詳しく説明されています。

Usage With TypeScript | Redux Toolkit

アプリの階層

アプリの階層は次のようになっています。
肝になるのはreduxディレクトリ以下のファイルです。

└── src
    ├── App.js
    ├── components
    │   ├── Counter.js
    │   ├── CounterBtn.js
    │   ├── CounterResult.js
    │   └── CounterSelect.js
    ├── index.js // ProviderコンポーネントでReduxの影響範囲を指定
    ├── logo.svg
    ├── redux
    │   ├── counterSlice.js //reducer、actionに関する設定
    │   └── store.js  // sroreに関する設定
    └── styles.css

Storeの構成

Storeにはcounterという名前のSliceが1つあります。
図にするとこんな感じです。

initialState(stateの初期値)のdisplaycountは画面に表示させる値で、settingcountは増減値になります。
reducerにstateの値を処理するコードを記述します。具体的には、値のプラス、マイナス、リセット、そして増減値のセットになります。
actionは[スライス名/タイプ名]で自動的に生成されます。

カウンターアプリ作成のポイント

カウンターアプリ作成のポイントを簡単にメモしておきます。

StoreにReducerを設定する

configureStore()でStoreを作成します。プロパティcounterに、createSlice()で作成するSliceを設定します。

redux/store.js

import { configureStore } from "@reduxjs/toolkit";
import countReducer from "./counterSlice";

export const store = configureStore({
  reducer: {
    counter: countReducer // Reducerを設定する
  }
});

SliceでReducerとActionCreatorを生成する

createSlice()でReducerとActionCreatorを生成します。

redux/counterSlice.js

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

// 初期値を設定
const initialState = {
  displaycount: 0,
  settingcount: 1,
};

const counter = createSlice({
  name: 'counter', 
  initialState,
  reducers: {
    plus(state, action) {
      console.log(action);
      state.displaycount += state.settingcount;
    },
    minus(state, action) {
      console.log(action.type, action.payload);
      state.displaycount -= state.settingcount;
    },
    reset(state, action) {
      console.log(action);
      state.displaycount = 0;
    },
    // PayloadAction タイプを使用して、`action.payload の内容を宣言`
    numSetting(action) {
      console.log(action.type, action.payload);
      state.settingcount = action.payload;
    },
  },
});

const { plus, minus, numSetting, reset } = counter.actions;

export { plus, minus, umSetting, reset };
export default counter.reducer;

最後にactionとreducerをエクスポートして、他のファイルで使用できるようにします。

React Toolkitを使ったReducerの設定

reducerは純粋関数である必要があります。
本来、次のように新しいオブジェクトを作成して、更新したstateをreturnで返します。

reducers: {
  plus(state, action) {
    const newPlus = {...state}
    newPlus.displaycount = state.displaycount +  state.settingcount;
    return newPlus // 更新したstateを戻り値で返す
  },
  ...省略
}

純粋関数とは

  • 副作用を持たない(ロジックが引数以外に依存せず、外部の状態を変更しない)
  • 同じ引数に対しては常に同じ結果を返す

 

React Toolkitの場合、このようにstateを直接編集できます。returnも必要ありません。

reducers: {
  plus(state, action) {
    state.displaycount += state.settingcount;
  },
  ...省略
}

ActionCreatorの設定

ActionCreatorは、createSlice()で自動的に生成されます。
createSlice()で生成したオブジェクト(今回はcounterオブジェクト)のactionsプロパティに含まれます。

// createSliceでreducerとactionクリエイターを生成
const counter = createSlice({
  name: 'counter',  // スライス名
  initialState: {
    displaycount: 0,
    settingcount: 1,
  },
  reducers: {
    // タイプ名でReducerを設定
    plus(state, action) {
      state.displaycount += state.settingcount;
    },
    ...省略
  }
})

// ActionCreatorを分割代入で取り出す
const { plus, minus, numSetting, reset } = counter.actions;

取得したActionCreatorは、分割代入で取り出すと使いやすいです。

なお、作成されたActionCreatorのタイプ名は、[スライス名/タイプ名]になります。
例){type: “counter/plus”, payload: 0 }

ReducerとActionCreatorをエクスポートする

最終的には、ReducerとActionCreatorをエクスポートします。
※Reducerはdefaultでエクスポートしています。

export { plus, minus, numSetting, reset };
export default counter.reducer;

StoreのReducerにcreateSlice()で作成したreducerを設定します。
今回、counterプロパティに設定しました。
defaultエクスポートしたオブジェクトは、名前を変更して読み込むことができるので、countReducerに名前を変更しました。

redux/store.js

import { configureStore } from "@reduxjs/toolkit";
import countReducer from "./counterSlice";

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

Stateを表示する

stateはuseSelector()で取得できます。stateはStoreのプロパティ名に格納されています。
次のように取り出します。

import { useSelector } from "react-redux";

const CounterResult = () => {
  const count = useSelector((state) => state.counter.displaycount);
  // const count = useSelector((state) => state.counter.displaycount);
  return <div className="result">{count}</div>;
};
export default CounterResult;

まとめ

今回、Reduxを理解するために簡単なカウンターアプリを作成しましたが、実際にはユーザーのログイン状態の管理に使うことが多いそうです。

アプリ作成には必須の知識となりそうなので、もう少し使い方に慣れたいと思います!