Node.js初心者がRedux勉強中です。よく分からなかったのでソースを読んでます。
createStore.jsのソース
最初に使うcreateStore()は、createStorejsにあります。
https://github.com/reduxjs/redux/blob/v4.0.1/src/createStore.js
createStore()は、Middlewareを使う場合と使わない場合で処理の流れが違っています。この記事では"Middlewareを使わない場合"の処理を追っかけます。"Middlewareを使う場合"の記事はそのうち書こうと思っています。 書きました。ReduxのcreateStore()の処理を追う(Middleware有りの場合)
この記事で解説する流れは、呼び出し元がこんな風に呼び出している場合を想定しています。
// stateの初期状態無し
let store = createStore(reducerA);
// stateの初期状態あり
let store = createStore(reducerA, preloadedState);
createStore()でやっている処理
引数の整理と型チェック
createStore()の最初は、引数preloadedStateとenhancerの内容を確認して、第二引数のpreloadedStateがfunctionだった場合には、これをenhancerとみなして変数の置き換えをやっています。
これは"Middlewareを使う場合"の呼び出し方に対応するためなので、Middlewareを使わないこの記事分では関係ありません。
関数の定義と変数の束縛
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
function getState() {...}
function subscribe(listener) {...}
function dispatch(action) {...}
function replaceReducer(nextReducer) {...}
createState()内で変数を保持しつつ、関数内関数としてgetState()、subscribe()、dispatch()、replaceReducer() を定義し、それぞれの関数の中で変数を束縛していきます。
こうすることで、同じ変数を中に持つ関数群が作成できます。
(この"束縛"関連の説明は、自分がJavaScriptの知識が曖昧なので自信がありません)
stateの初期化
dispatch({ type: ActionTypes.INIT })
ActionTypes.INITは、redux組み込みのアクションのようです。ソースはutils/actionTypes.jsです。
通常Reducerは「変更前stateがnullだったらデフォルト値を設定する」「知らないアクションが来たらstateをそのまま返す」実装のはずなので、ActionTypes.INITで初期化されます。
面白いのは、ActionTypes.INITはランダムな文字が付与されています。おそらく、ユーザの定義と被らないようにしているのだと思います。
またActionTypes.REPLACE や ActionTypes.PROBE_UNKNOWN_ACTION というアクションも定義されています。これらはcombineReducers.jsの中で使われています。
Storeオブジェクトを組み立てて返す
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
戻り値としては、関数内関数をオブジェクトに割り当てて返しています。
この戻し方で、呼び出し元では store.dispatch(〜)という、「オブジェクトのメソッド呼び出し」のような見た目で各関数にアクセス出来るようになります。各関数は共通の変数を束縛しているので、あたかも「Javaのクラスをnewして作るオブジェクト」のように扱えます。
ここで返される関数のうち、最後の[$$observable]:observable
は奇妙です。ここは今は追いかけない事にしています。
JavaScriptの仕様だと思うのですが、
return {
"dispatch": dispatch,
"subscribe": subscribe,
"getState": getState,
"replaceReducer": replaceReducer,
[$$observable]: observable
}
と書かなくても、ちゃんと関数名で呼び出せるようになるのですね。この辺はまだ良くわかっていません。