はじめに
Redux を使うにあたって避けて通れないのが Slice(スライス) です。
Redux Toolkit(RTK)が登場して以降、Redux の状態管理は「Slice」を中心に構成されるようになりました。
Sliceとは?
Slice とは「1つの機能領域の状態(State)と、それを更新するReducer・ActionCreatorをひとかたまりにしたもの」です。
Redux Toolkit の createSlice() を使えば action も reducer も一緒に管理できるのがGoodポイントです!
元々のReduxの面倒くさかったところ
旧来の Redux ではActionTypeの定義、ActionCreatorの作成、Reducerの定義をそれぞれ別々に行なっており、
同じ機能のコードが散らばるため複雑になりやすい のが課題でした。
// ActionTypeの定義
const INCREMENT = 'counter/increment'
// ActionCreatorの作成
function increment(amount: number) {
return {
type: INCREMENT,
payload: amount,
}
}
// 実際に作成されたAction
const action = increment(3)
・・・略
// Reducer関数
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'increment':
return { ...state, value: state.value + 1 }
case 'decrement':
return { ...state, value: state.value - 1 }
default:
return state
}
}
createSlice登場
createSlice関数は下記のような引数をとります
function createSlice({
// A name, used in action types
name: string,
// The initial state for the reducer
initialState: State,
// An object of "case reducers". Key names will be used to generate actions.
reducers: Record<string, ReducerFunction | ReducerAndPrepareObject>,
// A "builder callback" function used to add more reducers
extraReducers?: (builder: ActionReducerMapBuilder<State>) => void,
// A preference for the slice reducer's location, used by `combineSlices` and `slice.selectors`. Defaults to `name`.
reducerPath?: string,
// An object of selectors, which receive the slice's state as their first parameter.
selectors?: Record<string, (sliceState: State, ...args: any[]) => any>,
})
先ほどのcounterの例をcreateSliceで書いてみます
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
// ActionTypeに使われるname
name: 'counter',
// Reducerのデフォルト状態値
initialState: { value: 0 },
// Reducer
reducers: {
// ActionTypeがcounter/incrementになる。(name/case名がActionTypeになる)
increment(state) {
state.value += 1
},
decrement(state) {
state.value -= 1
},
addByAmount(state, action) {
state.value += action.payload
},
},
})
export const { increment, decrement, addByAmount } = counterSlice.actions
export default counterSlice.reducer
React コンポーネントから使う場合はこのように書きます
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement } from './counterSlice'
export const Counter = () => {
const value = useSelector((state) => state.counter.value)
const dispatch = useDispatch()
return (
<div>
<p>value: {value}</p>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
</div>
)
}
終わりに
最後まで読んでいただきありがとうございました!
初めて実務でSliceを見かけた時は「あ、これがフロントの状態管理の心臓だ…」と直感で感じたのが記憶に新しいです…!