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

Redux ToolKitで提供されているSliceを用いると、Reduxを使う際、ActionとReducerを1つにまとめて記述できます。また、ステートの更新の際にイミュータブルでない書き方をしても値の更新がされます。そんな何かと便利なSliceの使い方について、最小限の構成でシンプルに紹介します。
Sliceを使うにはRedux ToolKitが必要です。コンソールから追加します。
npm install @reduxjs/toolkit
冒頭でも述べましたが、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を定義します。
上記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でリデューサとアクションを同時に記述しています。
コードのように、第一引数には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に相当する処理の記述が終わりました。
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
この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を使う単純な例になります。このようにとてもシンプルに分かりやすい記述ができます。
実際は他のステートを利用したりより複雑な記述が必要になるかと思いますが、この基本さえ抑えておけば応用が利きます。
より複雑な使い方は、公式ドキュメントを参照下さい。