reactjs
flowtype
recompose

recompose と ReactRedux.connect の HOC を Flow で合成して型付けする

flow@60.1

これらの型定義の導入を前提とする

ユーティリティタイプや段階的な推論を組み合わせるテクニカルなコードになったが、一度理解してしまえば型付けされた 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 の定義の取り出しは、コード中で完結したかっただけなので真似する必要はない。