💡全体像をざっくりを理解する
登場人物
Store
あらゆる状態を管理している倉庫のような奴
Reducer
Store
にある状態を更新する奴
Store
にある状態を更新できるのはReducer
だけ
倉庫(Store
)の中にいる仕分け人のようなイメージ
Reducer
が状態を更新するときは、現在のstate
とaction
を受け取る
action
に応じて新しい状態を返す
dispatch
Reducer
に現在のstate
とaction
を届けるだけな奴
「こういう注文(action
)来てます〜。現在のstate
(状態)もお届けしました〜。 Reducer
さん、後はよろしくです〜。」なイメージ
action
dispatch
される注文伝票みたいな奴
action
(注文伝票)をReducer
に届けることで、Reducer
はaction
に応じて新しい状態を返す
例えばカウントを制御するアプリケーションの場合、
「カウント増やして〜」とか「カウント減らして〜」などのaction
がある
state
状態のこと。Store
で管理されている。
全体の流れ
ここではわかりやすいようにカウントを制御するだけのReactアプリケーションを想定します。
- ユーザー「カウント+1ボタンポチッと」
- 現在の
state
(count: 0)とaction
(カウント増やして~)をStore
にdispatch
(送る)する -
Store
にいる仕分け人Reducer
が 現在のstate
(count: 0)とaction
(カウント増やして~)を受け取る -
Reducer
「えぇーと、現在のstate
(count: 0)とaction
が"カウント増やして〜"ね、OK! 」 -
Reducer
「はい完了!state
(count: 1)」 -
React
「お!state
が更新された。よっしゃー画面を新しい状態に更新するぞー」 - ユーザー「カウントが"1"になった〜」
💡Redux ToolKitで全体像を理解する
アプリは単一のコンポーネントと単一のストアを持つ
Reactアプリケーションが階層の一番上に単一のコンポーネントを持っているように、Store
も単一です。
1つのアプリケーションで状態を管理するStore
は1つ。
全てのstate
を1つのStore
で管理し、必要なデータを適宜取り出せる
state
をコンポーネントツリーの外部にあるStore
で持つイメージ
アプリの状態は単一のStore
内のオブジェクトツリーに保存される
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app'),
)
Slice
Store
が倉庫だとしたらSlice
は倉庫の中にある棚のようなイメージ
アプリケーションで扱う状態に応じてSlice
を切り分けることで、状態の管理がしやすくなる。
例えばcountを扱うSlice
やtodoを扱うSlice
などに分けることができる
createSlice
は state
,reducer
,action
をまとめて作成する関数(便利!!)
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
const initialState = {
value: 0,
}
// Sliceの作成
export const counterSlice = createSlice({
name: 'counter', // actionの名前の一部になる
initialState, // stateの初期状態
reducers: {
increment: (state) => { // 状態を更新する関数(actionの名前の一部になる)
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
// 非同期処理を扱うときに使用する
extrareducer: ...
})
// コンポーネントからactionをdispatchできるようにexport
export const { increment, decrement } = counterSlice.actions
// コンポーネントからstateを参照するための関数をexport
export const selectCount = (state) => state.counter.value
// Storeに登録するためにexport
export default counterSlice.reducer
state
state
はinitialState
に記述する
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
//省略
initialState,
//省略
})
こう書いてもOK
export const counterSlice = createSlice({
//省略
initialState: {
value: 0,
}
//省略
})
reducer
reducers
の中に状態を変更する関数をまとめる
increment
とdecrement
がreducer
にあたる
export const counterSlice = createSlice({
//省略
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
//省略
})
action
action
の名前はname/reducer
というような形になる(Redux DevToolsでみると分かりやすい)
下記で言うとcounter/increment
のような形になる
ただし、Redux ToolKitが内部でよしなに色々とやってくれるので、reducers
に登録したreducer
の名前を下記のようにexport
してあげればOK
export
したaction
をコンポーネントからdispatch
することで状態が更新される
export const counterSlice = createSlice({
name: 'counter',
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
})
// コンポーネントからactionをdispatchできるようにexport
export const { increment, decrement } = counterSlice.actions
SliceをStoreへ結合
Store
に登録するReducer
のkey
でstate
にアクセスできる
Reducer
のキーがcounter
なのでstate.counter
でアクセスできる
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
状態の参照(useSelector)
useSelector
...state
に変更があったら自動的に再実行され、コンポーネントを再描画する
import { useSelector } from 'react-redux'
import { selectCount } from '../features/counter/counterSlice'
const count = useSelector(selectCount)
const Counter = () => {
return(
<>
<button>クリック!!</button>
<span>{count}</span>
</>
)
}
状態の更新(useDispatch)
useDispatch
はdispatch
関数を返す
action
を引数にdispatch
関数を実行することでReducer
が作動し、state
が更新される
action
に引数を渡したらReducer
でaction.payload
として値を受け取れる
import { useDispatch, useSelector } from 'react-redux'
import { selectCount, increment } from '../features/counter/counterSlice'
const dispatch = useDispatch()
const count = useSelector(selectCount)
const Counter = () => {
return(
<>
<button onClick={()=> dispatch(increment())}>クリック!!</button>
<span>{count}</span>
</>
)
}
💡全体の流れを簡単におさらい
- アプリは単一のコンポーネントと単一のストアを持つ
-
Slice
を作成してStore
へ結合する -
useSelector
を使用して状態の参照ができる -
useDispatch
を使用して状態の更新ができる
Tips
はるか昔、action
を作るのに色々とごちゃごちゃしていましたが、 action
の作成に関してあまり意識しなくても良くなりました。
Redux ToolKitが中でいい感じに仕事してくれてると思って頂ければ良いと思います。興味がある方はググって見て下さい。