これらの型定義の導入を前提とする
- https://github.com/acdlite/recompose/blob/master/types/flow-typed/recompose_v0.24.x/flow_v0.55.x-/recompose_v0.24.x.js
- https://github.com/flowtype/flow-typed/blob/master/definitions/npm/redux_v3.x.x/flow_v0.55.x-/redux_v3.x.x.js
ユーティリティタイプや段階的な推論を組み合わせるテクニカルなコードになったが、一度理解してしまえば型付けされた props が手に入る。
/* @flow */
import React from 'react'
import { bindActionCreators, combineReducers } from 'redux'
import { compose, lifecycle, pure, type HOC } from 'recompose'
import { connect } from 'react-redux'
// reducer
const INC = 'inc'
const inc = () => ({ type: INC })
type Counter = {
value: number
}
type Action = $Call<typeof inc>
const counter = (state: Counter = { value: 0 }, action: Action): Counter => {
switch (action.type) {
case INC: {
return { value: state.value + 1 }
}
default: {
return state
}
}
}
const rootReducer = combineReducers({ counter })
// Get RootState from result of rootReducer
type RootState = $Call<typeof rootReducer>
// compose HOCs
type OuterProps = {
foo: string
}
type Props = {
foo: string,
bar: number,
actions: {
inc: Function
}
}
const connector = connect(
(state: RootState, props) => {
return {
foo: props.foo,
bar: state.counter.value
}
},
dispatch => bindActionCreators({ inc }, dispatch)
)
const enhancer: HOC<Props, OuterProps> = compose(
connector,
pure,
lifecycle({
componentDidMount() {
console.log('mounted')
}
})
)
// props is resolved by hoc
const MyComponent = enhancer(props => {
return (
<div>
{props.foo}
{props.bar}
{props.actions.inc}
</div>
)
})
変数の意図
- OuterProps: component の外から受ける props の型
- Props: 内部で解決される props の型
- RootState: reducer が返す state の型
- connector: Redux の connect の hoc
- enhancer: recompose.compose で合成された結果のhoc関数
これらの用語は redux の内部表現やflowの型定義から借りてきた
見るべき箇所
flow環境でやるとわかるが、connect の mapStateToProps 相当の関数の中と、最後に enhancer でラップした SFC の props が補完されたり静的に追跡されている。
RootState の定義の取り出しは、コード中で完結したかっただけなので真似する必要はない。