Reduxをソースコードから理解する その2

[Reduxをソースコードから理解する その1](http://qiita.com/juntaki/items/d7b44fd9c2c35ea9ce24)の続きです。
reactと連携するときに使うreact-Redux (5.0.3: 1c714abc1763)のコードから、Storeと`dispatch()`の使い方を理解します。Redux本体より複雑かもしれません。

[react-redux/api.md at master · reactjs/react-redux](https://github.com/reactjs/react-redux/blob/master/docs/api.md)

# Provider

Providerはreact-Reduxが提供するReact Componentです。
ReduxのStoreをReactから参照するために使います。Providerにはstoreをpropsとして渡します。

~~~jsx:sample
ReactDOM.render(
  <Provider store={store}>
    <MyRootComponent />
  </Provider>,
  rootEl
)
~~~

ReactのContextによって`store`と`storeSubscription`を子要素に伝えます。
Contextの利点はデータをpropsとして明示的に渡さなくても子要素に伝えることができる点です。

>In some cases, you want to pass data through the component tree without having to pass the props down manually at every level. You can do this directly in React with the powerful "context" API.
[Context - React](https://facebook.github.io/react/docs/context.html)

ProviderはContextの親の実装になっています。

>親で static childContextTypes = {...} と getChildContext() を実装する
子で static contextTypes = {...} を実装する
子で this.context は getChildContext の結果を参照できる
[React の Context を使って Flux を実装する - Qiita](http://qiita.com/mizchi/items/ef3fe8a82ce0fb49b52d)


~~~javascript:Provider.js
export default class Provider extends Component {
  getChildContext() {
    return { store: this.store, storeSubscription: null }
  }

  constructor(props, context) {
    super(props, context)
    this.store = props.store
  }

  render() {
    return Children.only(this.props.children)
  }
}

Provider.childContextTypes = {
  store: storeShape.isRequired,
  storeSubscription: subscriptionShape
}
~~~

Providerの子要素はユーザがContextを受け取れるように実装する必要があります。それはreact-Reduxの`connect()`によって自前のReact Componentをラップすることで実現できます。

# connect()

react-Reduxの`connect`は`createConnect()`をデフォルト引数によって呼び出した戻り値で、別の挙動をさせることも可能です。
[ドキュメント](https://github.com/reactjs/react-redux/blob/master/docs/api.md)にもあるように、デフォルトでは`connectAdvanced()`への[Facade](https://ja.wikipedia.org/wiki/Facade_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)です。コードもバッサリ削れば見通しが良くなります。詳細は次回の予定です。

~~~javascript
// createConnect with default args builds the 'official' connect behavior. Calling it with
// different options opens up some testing and extensibility scenarios
export function createConnect({
  connectHOC = connectAdvanced,
・・・
} = {}) {
  return function connect(
・・・
  ) {

    return connectHOC(
・・・
    })
  }
}

export default createConnect()
~~~


# connectAdvanced()

Providerの子要素としてContextが受け取れる`Connect`で自前のComponentをラップします。
`connectAdvanced()`の戻り値、遡ると、`connect()`の戻り値は`wrapWithConnect()`です。つまりサンプルコードによくある、`export default connect()(TodoApp)`は`wrapWithConnect(TodoApp)`となります。[hoistStatics](https://github.com/mridgway/hoist-non-react-statics/blob/v1.2.0/index.js#L29)はちょっとプロパティをコピーする程度なので、実質的な戻り値は`Connect`のほうです。
`Connect`の`render()`を見ると、React Componentとして表示されるのは`WrappedComponent`とわかります。

~~~javascript:PropTypes.js
export const storeShape = PropTypes.shape({
  subscribe: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  getState: PropTypes.func.isRequired
})
~~~

~~~javascript:connectAdvanced.js
export default function connectAdvanced(
・・・
) {
・・・
  const contextTypes = {
    [storeKey]: storeShape,
    [subscriptionKey]: subscriptionShape,
  }

  return function wrapWithConnect(WrappedComponent) {
    class Connect extends Component {
・・・
      render() {
・・・
        if (selector.error) {
          throw selector.error
        } else {
          return createElement(WrappedComponent, this.addExtraProps(selector.props))
        }
      }
    }

    Connect.childContextTypes = childContextTypes

    return hoistStatics(Connect, WrappedComponent)
  }
~~~

# あとがき

`Provider`と`connect()`によってReactのContextを通してstoreを伝える大まかな構造を見ました。
次回は`connect()`の引数として渡した`mapStateToProps`と`mapDispatchToProps`をどう使っていくのか、詳細に読んでいく予定です。