Reduxの全体像をざっくり理解する奴

💡全体像をざっくり理解する
登場人物
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が中でいい感じに仕事してくれてると思って頂ければ良いと思います。興味がある方はググって見て下さい。
Discussion