【入門】ReduxでReactの状態管理をする!その使い方とメリットを解説。

【入門】ReduxでReactの状態管理をする!その使い方とメリットを解説。

この記事では、Reactとともによく使用される状態管理ライブラリReduxについてその特徴やメリット、また、基本的な使い方をReactの最新バージョンである18を基に説明したい。

近年、複雑さを増したWebアプリケーションの実装。バグの温床にもなりやすいステート管理においては特にシンプルで即効性のある解決策が求められてきている。

Reduxはそんなステート管理の問題点を解消するライブラリ。フロントエンドの開発者であればその概要をしっておいて損は無いだろう。

では、見ていこう!

Reduxとは?

まずはReduxについて説明していこう。

ReduxはJavaScriptのライブラリであり、メモリ内に保存されるオブジェクトの状態を管理するのに特化している。

Fluxアーキテクチャの最大の長所は、アプリケーション内の複数のコンポーネントが状態をアップデートすることによって発生するデータの不整合を妨げることができるという点だ。

Facebookによって提唱されたFluxアーキテクチャを基に設計されている。Fluxアーキテクチャを実装するためのフレームワークは「Reflux」「Flummox」など数多く存在するが、もっともよく導入されているのがReduxである。

ReduxはReactのためのライブラリというイメージが強いが、AngularやVueといった他のJSフレームワークでも使用することができる。

Reduxホームページ:: https://redux.js.org

Fluxアーキテクチャ

Fluxアーキテクチャについて説明したい。

下の図はFluxアーキテクチャにおけるデータフローを表している。

Fluxアーキテクチャにおいてはデータフローが常にひとつの方向に流れていく。

図の構成要素を説明すると以下のようになる。

  • Action ボタンクリックや文字入力などのユーザアクション。
  • Dispatcher 更新の指示をおこなう関数。アクションによってトリガーされる。
  • Store 状態を保持するオブジェクト。
  • View  Storeの更新に伴い、ビューがアップデートされる。

Fluxアーキテクチャにおいては、Dispatcher以外の存在がStoreを書き換えることを制限する。

従来の状態管理の問題点とReduxのメリット

Webフロントエンドにおける従来の問題点を今一度整理したい。

フロントエンドでは、ひとつの状態(パラメータ)を複数の異なるコンポーネントで管理している。

開発者はプログラムへの変更が他のコンポーネントにどのような副作用をもたらすか注意しないといけないうえに、エラーが発生した際にどのコンポーネントが原因であるか突き止めるのが困難だ。

Fluxアーキテクチャでは、状態変更をトリガーするのがDispatherのみであるためどのコンポーネントが原因か突き止めるのが簡単であり、また状態の不整合を防ぐことができる。

  • バグを減らすことができ、エラーの原因を追究するのが容易になる
  • アプリとステート管理を切り離すことができコードの見通しが良くなる
  • 型を作ることによりコードクオリティのバラつきを減らすことができる

Reduxの使い方

では、Reduxを自身のアプリで使ってみよう。

なお、ここでの解説はReactの最新バージョンであるバージョン18を基準としている。

Redux・React Redux・Redux Toolkitのインストール

Reduxのインストールはnpmコマンドでおこなう。

ここではReduxの他に以下の2つのライブラリをインストールする。

  • React Redux – ReduxをReactアプリに取り込むためのバインダー
  • React Toolkit – Reduxのための公式ライブラリ。Redux Toolkitは使用することが推奨されている。
npm install redux react-redux @reduxjs/toolkit

Reduxをアプリにインポートする

まずはsrcフォルダ内にRedux Storeを統括するためのファイルであるstore.jsを作成する。

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

const store = configureStore();

export default store

index.jsにてReact ReduxのProviderとstore.jsをインポートする。

AppタグをProviderタグで囲む。これによってRedux Storeがアプリで使用することができるようになる。

import React from 'react'
import ReactDOM from 'react-dom/client'

import { Provider } from 'react-redux'
import store from './store'

import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <Provider store={store}>
    <App />
  </Provider>
)

状態をSliceに定義する

ここからは状態やそれらに対するアクションをSliceファイルに定義していく。

まずはここで使用する用語について簡単に解説したい。

  • State – 状態を保持するオブジェクト。アプリの使用中に値は変化する。
  • Action – 更新の内容・タイプを指示するオブジェクト。
  • Reducer – Stateをアップデートする関数。StateとActionを引数として持つ。
  • Slice – Redux Toolkitに含まれる状態のデフォルト値やアクションを定義するオブジェクト

コードを見たほうが分かりやすいと思うので、実際にコードを書いてみよう。

以下の例はショッピングカートの状態を管理するSliceの例だ。

import { createSlice } from '@reduxjs/toolkit'

const initialState = {
    items: []
}

const cartSlice = createSlice({
    name: 'cart',
    initialState,
    reducers: {
        addItem(state, action) {
            state.item = [...state.item, { ...action.payload }];
        },
        deleteItem(state, action) {
            state.item = state.item.filter(item => item.id !== action.payload.id);
        },
        resetCart(state, action) {
            state.item = [];
        }
    },
})

export const { addItem, deleteItem, resetCart } = cartSlice.actions
export default cartSlice.reducer

ここではコードをシンプルにするために商品の中身が同じであるかのチェックはしていない。

作成したReducerをstore.jsファイルにインポートして、configureStore関数の引数として渡す。

import cartReducer from './reducers/cartSlice';

const store = configureStore({
  reducer: {
    cart: cartReducer
  }
})

dispatch関数でStateを変更する

では、dispatch関数を使用して前項で作成したReducerを呼び出してみる。

プロジェクト内の任意の場所で以下のような具合にdisptachイベントを使用する。

import { useDispatch } from 'react-redux'
const dispatch = useDispatch()

import { addItem } from './reducers/cartSlice'
dispatch(addItem('A13457'));

うまくいけばカート内にアイテムが追加されるはずだ。

Redux DevTools

ReduxをGoogle Chromeで使用するなら、Chromeの拡張機能である「Redux DevTools」をインストールするようにしよう。

Redux DevToolsでは、状態の変化をDispatchごとに追跡することができる。

エラーを探す際には強力なツールとなるだろう。