thumbnail

【React】Sliceを用いたReduxの簡単な実装方法

Redux ToolKitで提供されているSliceを用いると、Reduxを使う際、ActionとReducerを1つにまとめて記述できます。また、ステートの更新の際にイミュータブルでない書き方をしても値の更新がされます。そんな何かと便利なSliceの使い方について、最小限の構成でシンプルに紹介します。

Redux ToolKitの準備

Sliceを使うにはRedux ToolKitが必要です。コンソールから追加します。

npm install @reduxjs/toolkit

Sliceファイルの定義

冒頭でも述べましたが、SliceとはReducerとActionを組み合わせたものです。

ステートの例

次の「string型のname」と「number型のage」のステートを更新する処理を考え、Sliceの具体的な使用方法について解説していきます。

interface PersonState {
  name: string
  age: number
}

初期値を適当に決めます。なお、後から別ファイルで利用するためexportしておきます。

export const initialState: PersonState = {
  name: 'taro',
  age: 20
}

このstateに対して2つのactionを定義します。

  • ① ageを+1する
  • ② 名前を入力された値に変える

Sliceの書き方

上記2つの操作に対するSliceは次のように記述します。

const PersonSlice = createSlice({
  name: 'person',
  initialState,
  reducers: { //リデューサ+アクション
    incrementAge(state) {
      state.age++
    },
    changeName: (state, action: PayloadAction<string>) =>{
      state.name = action.payload
    }
  }
})

nameにこのスライスの名前、
initialStateで初期値を設定し、
reducersでリデューサとアクションを同時に記述しています。

  • 1つめの incrementAgeはageを+1する処理です。
  • 2つめのchangeNameは、nameをdispatchされた値に変更する処理です。

コードのように、第一引数にはstate、第二引数にはactionを指定します。actionはPayloadAction<payloadの型>で型付けしています。

そして、コードのようにイミュータブルな値の操作でstateの更新が可能となっています。これは内部的にImmerという「イミュータブルな操作をミュータブルな操作に変換する」ライブラリを使っているので可能となっています。

最後にこのSliceからリデューサとアクションを切り離します。

//アクションの切り出し
export const { incrementAge, changeName } = PersonSlice.actions

//リデューサの切り出し
export const PersonReducer = PersonSlice.reducer

以上をまとめると、Sliceのファイルは次のようになります。

import { createSlice, PayloadAction } from '@reduxjs/toolkit'

interface PersonState {
  name: string
  age: number
}

//初期値
export const initialState: PersonState = {
  name: 'taro',
  age: 20
}

//スライス
const PersonSlice = createSlice({
  name: 'person',
  initialState,
  reducers: { //リデューサ+アクション
    incrementAge(state) {
      state.age++
    },
    changeName: (state, action: PayloadAction<string>) =>{
      state.name = action.payload
    }
  }
})

//アクションの切り出し
export const { incrementAge, changeName } = PersonSlice.actions

//リデューサの切り出し
export const PersonReducer = PersonSlice.reducer

たったこれだけでReducerとActionに相当する処理の記述が終わりました。

Storeファイル

Redux ToolKitのconfigureStoreを使うと簡単に記述できます。
使用方法を次のコードに示します。

import { configureStore } from "@reduxjs/toolkit"
import { initialState as personInitialState, PersonReducer } from "./PersonSlice"

export class RootState {
  person = personInitialState
}

//リデューサ
const reducer = {
  person: PersonReducer
}

//ストア
const store = configureStore({
  reducer
})

export default store

Componentファイル

このSliceを利用するコンポーネントの例を記載します。

import { FC, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { incrementAge, changeName } from "./PersonSlice"
import { RootState } from "./Store"

const App: FC = () => {
  // ストアからstate取得
  const person = useSelector((state: RootState) => state.person )
  // dispatch
  const dispatch = useDispatch()

  //フォームの入力値
  const [name, setName] = useState(person.name)

  //dispatchはJSX内で行っている
  return (
    <>
      <p>name: {person.name}</p>
      <p>age: {person.age}</p>

      {/* incrementAgeのdispatch */}
      <button onClick={() => dispatch(incrementAge())}>age + 1</button>

      {/* changeNameのdispatch */}
      <input  value={name} onChange={e => setName(e.target.value)}/>
      <button onClick={() => dispatch(changeName(name))}>change name</button>
    </>
  )
}

export default App

まとめ

以上がSliceを使う単純な例になります。このようにとてもシンプルに分かりやすい記述ができます。
実際は他のステートを利用したりより複雑な記述が必要になるかと思いますが、この基本さえ抑えておけば応用が利きます。

より複雑な使い方は、公式ドキュメントを参照下さい。