LoginSignup
0
0

More than 1 year has passed since last update.

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

Posted at

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

対象読者

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

redux まとめ

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

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

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

React で呼び出している redux は、reducer をまとめる combineReducerscreateStore です(細かい redux の使い方は省きます)。

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

export const reducers = combineReducers({hoge: hoge})
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 から。

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
  }
}

ここでは、上記の

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

createStore

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

ソースコードは、src/createStore.ts から。

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

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 は、それぞれ「変更があるたびに呼び出す関数の登録」と「reducer の変更」ができます。

今回は、この中の getStatedispatch を見てみましょう。

まずは、getState()

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

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

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

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 で出てきた

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 などありますが、大体、内容は以上になります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0