試しに redux を読んでみたので、そのまとめ。
この記事では、react-redux を除いた redux、つまり createStore を使った getState・dispatch 操作を、実際の redux のコードを読んで理解してみます。
対象読者
redux の使い方を知っている人
redux まとめ
まずは、簡単な概要を説明します。
redux は 状態管理 のライブラリ。
React の State状態管理 のバケツリレーを改善するために、大きな State を作っておき そこにデータアクセス(react-redux)することで状態管理をする。ここでは「大きな State を作る」部分である redux だけを解説します(暇があれば、後半の「データアクセス」部分をしている react-redux も読んでみます)。
React で呼び出している redux は、reducer をまとめる combineReducers
と createStore
です(細かい redux の使い方は省きます)。
import {createStore} from "redux"
import {reducers} from "reducers"
import {someAction} from "actions"
・・・省略
store = createStore(reducers)
store.dispatch(someAction)
console.log(store.getState())
import {combineReducers} from "redux"
import {hoge} from "./hoge"
export const reducers = combineReducers({hoge: hoge})
export const hoge = (action, state) => {
switch(action.type) {
case: "HOGE":
/* do some stuffs */
return new_state
default:
return state
}
}
combineReducers
では、まずは combineReducers
から読んでみましょう。
ソースコードは、 redux:src/combineReducers.ts から。
function getUnexpectedStateShapeWarningMessage(...){
/* stateの型を確認する関数 */
}
function assertReducerShape(...){
/* reducerの型を確認する関数 */
}
export default function combineReducers(reducers: ReducersMapObject) {
const reducerKeys = Object.keys(reducers)
const finalReducers: ReducersMapObject = {}
for (let i = 0; i < reducerKeys.length; i++) {
/* 関数なら、finalReducers に reducer を登録 */
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
・・・reducer の型チェック
return function combination(state, action){
let hasChanged = false
const nextState: StateFromReducersMapObject<typeof reducers> = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
/* reducer を 実行 */
/* 上の例で言えば、hoge の switch文 */
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
/* state が 更新された */
nextState[key] = nextStateForKey
/* 更新されたか確認 hasChanged */
}
return hasChanged ? nextState : state
}
}
ここでは、上記の
export const hoge = (action, state) => {
switch(action.type) {
case: "HOGE":
/* do some stuffs */
return new_state
default:
return state
}
}
などの reducer を受け取って、まずは function の reducer のみのオブジェクト finalReducers を作っています。
そして、後で出てきますが 実際に 引数(action, state) で呼び出された場合(クロージャーになっています) に、finalReducers の1つ1つの reducer に 引数(action, state) を渡し、reducer で state を更新します(上の hoge.js のような形で)。
createStore
次に、createStore
を読んでみましょう。
ソースコードは、src/createStore.ts から。
createStore では、主に getState・subscribe・dispatch・replaceReducer の4つの関数があります。
export default function createStore(reducer, preloadedState? , enhancer?) {
・・・省略
let currentReducer = reducer
let currentState = preloadedState
let currentListeners: (() => void)[] | null = []
let nextListeners = currentListeners
let isDispatching = false
・・・省略
function getState(){
・・・省略
}
function subscribe(listener){
・・・省略
return function unsubscribe(){
・・・省略
}
}
function dispatch(action){
・・・省略
}
function replaceReducer(nextRuducer){
}
}
ここで出てくる getState() と dispatch(action) は 使ったことがあるかもしれませんね。react-redux がないと、この 2つの関数を使って「State の取得」と「State の更新」をしていましたね。
他の subscribe と replaceReducer は、それぞれ「変更があるたびに呼び出す関数の登録」と「reducer の変更」ができます。
今回は、この中の getState
と dispatch
を見てみましょう。
まずは、getState()
function getState(){
・・・省略
return currentState
}
これは簡単ですね。単に currentState を返しているだけです。
では次に dispatch
を見てみましょう。
let currentReducer = reducer
・・・省略
function dispatch(action){
・・・型チェック
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
・・・subscribe() で登録した listener の呼び出し
return action
}
dispatch では、combineReducers した currentReducer
でクロージャーされた関数を呼び出しています。
なので前の currentReducer
で出てきた
return function combination(state, action){
・・・省略
for (let i = 0; i < finalReducerKeys.length; i++) {
・・・省略
const nextStateForKey = reducer(previousStateForKey, action)
・・・省略
}
return hasChanged ? nextState : state
}
の関数がそれに当たり、state の更新を行なっています。
他にも applyMiddleware などありますが、大体、内容は以上になります。