LoginSignup
7
7

More than 5 years have passed since last update.

手っ取り早くReduxがどのようなものか理解する~Store編~

Last updated at Posted at 2016-04-24

まとめ

  • createStore => store objectを返す。
  • dispatch => reducerを実行して新しいstateを作る。stateが変化した時に実行する処理(listener)を実行する。
  • subscribe => stateが変化した時に実行する処理(listener)を登録する。
  • replaceReducer => reducerを入れ替える
  • observable => subscribe時に実行した処理を登録する

Redux

Fluxを実装した一番人気のあるフレームワーク。
開発者がReact Core Teamでも一番熱心に活動している人のようなので安心して使える。
Facebookのアプリでも使用されていたので公式に推奨されているように感じる。

Store

Reduxのメインとなる機能。
createStoreで作成される。
https://github.com/reactjs/redux/blob/master/src/createStore.js

  • reducer
  • initialState
  • enhancer

これら3つを引数として作成される

createStore
export default function createStore(reducer, initialState, enhancer) {
  ...,
  if (typeof enhancer !== 'undefined') {
    ...,

    return enhancer(createStore)(reducer, initialState)
  }

  var currentReducer = reducer
  var currentState = initialState

  ...,
}

この通り、enhancerはcreateStoreを引数にとって新しいcreateStoreを生成するようなfunctionであることがわかる。wrapperに近い?
ここからcurrentReducerとcurrentStateがreducerとstateに置き換わる。

createStore
export default function createStore(reducer, initialState, enhancer) {
  ...,

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

上記より、各methodが入ったObjectがstoreということがわかる。

createStore
  ...,

  function dispatch(action) {
    ...,

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    var listeners = currentListeners = nextListeners
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]()
    }

    return action
  }

  ...,

ここからreducerはstateとactionを引数にとり、新しいstateを返すようなfunctionであることがわかる。
listenerは何かというと、subscriptionされているfunctionであることがわかる。

createStore
  ...,

  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected listener to be a function.')
    }

    var isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      var index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  ...,

返り値を保持することで登録したlistenerをunsubscribeすることが可能。
このような返り値で登録したものを削除する処理はReact界隈ではよく見るので、覚えていて損はなさそう。

createStore
  ...,

  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.INIT })
  }

  ...,

名前の通り、reducerを入れ替える処理。入れ替え時に、INITのActionが走る。
大体の実装ではswicthのdefaultケースを返す感じかな。

createStore
  ...,

  function observable() {
    var outerSubscribe = subscribe
    return {
      subscribe(observer) {
        if (typeof observer !== 'object') {
          throw new TypeError('Expected the observer to be an object.')
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        var unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }

観測用にsubscribeしています。
普通にsubscribeするのと何が違うのかは登録時に、observerが実行されることくらい。

最後に

言語の状態を変更するreducerと、各componentのsetStateを登録して全部のコンテンツを変換するようなものが簡単に出来そうな気がしました。

middleware, combineReducers編に続く・・・(完)

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