redux のソースコード を読んでみる

試しに redux を読んでみたので、そのまとめ。
この記事では、react-redux を除いた [redux](https://github.com/reduxjs/redux)、つまり createStore を使った getState・dispatch 操作を、実際の redux のコードを読んで理解してみます。

## 対象読者

redux の使い方を知っている人

## redux まとめ

まずは、簡単な概要を説明します。

redux は 状態管理 のライブラリ。

React の State状態管理 のバケツリレーを改善するために、大きな State を作っておき そこにデータアクセス（[react-redux](https://github.com/reduxjs/react-redux)）することで状態管理をする。ここでは「大きな State を作る」部分である [redux](https://github.com/reduxjs/redux) だけを解説します（暇があれば、後半の「データアクセス」部分をしている [react-redux](https://github.com/reduxjs/react-redux) も読んでみます）。

React で呼び出している redux は、reducer をまとめる `combineReducers` と `createStore` です（細かい redux の使い方は省きます）。

```javascript:index.js
import {createStore} from "redux"
import {reducers} from "reducers"
import {someAction} from "actions"
・・・省略
store = createStore(reducers)
store.dispatch(someAction)
console.log(store.getState())
```

```javascript:reducers/index.js
import {combineReducers} from "redux"
import {hoge} from "./hoge"

export const reducers = combineReducers({hoge: hoge})
```

```javascript:reducers/hoge.js
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](https://github.com/reduxjs/redux/blob/master/src/combineReducers.ts) から。

```javascript: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
  }
}
```

ここでは、上記の

```javascript:reducers/hoge.js
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 の１つ１つの reducer に 引数(action, state) を渡し、reducer で state を更新します（上の hoge.js のような形で）。

## createStore

次に、`createStore` を読んでみましょう。

ソースコードは、[src/createStore.ts](https://github.com/reduxjs/redux/blob/master/src/createStore.ts) から。

createStore では、主に getState・subscribe・dispatch・replaceReducer の４つの関数があります。

```javascript:createStore.ts
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 は、それぞれ「[変更があるたびに呼び出す関数の登録](https://www.yoheim.net/blog.php?q=20191201#t10)」と「reducer の変更」ができます。

今回は、この中の `getState` と `dispatch` を見てみましょう。

まずは、`getState()`

```javascript:createStore.ts
function getState(){
  ・・・省略
  return currentState
}
```

これは簡単ですね。単に currentState を返しているだけです。

では次に `dispatch` を見てみましょう。

```javascript:createStore.ts
let currentReducer = reducer
・・・省略
function dispatch(action){
  ・・・型チェック
  try {
    isDispatching = true
    currentState = currentReducer(currentState, action)
  } finally {
    isDispatching = false
  }

  ・・・subscribe() で登録した listener の呼び出し

  return action
}
```

dispatch では、combineReducers した `currentReducer` でクロージャーされた関数を呼び出しています。
なので前の `currentReducer` で出てきた 

```javascript:src/combineReducers.ts
  return function combination(state, action){
    ・・・省略
    for (let i = 0; i < finalReducerKeys.length; i++) {
      ・・・省略
      const nextStateForKey = reducer(previousStateForKey, action)
      ・・・省略
    }
  return hasChanged ? nextState : state
  }
```

の関数がそれに当たり、state の更新を行なっています。

他にも applyMiddleware などありますが、大体、内容は以上になります。