この記事では、 TypeScript で React Redux の connect
を使う際の知見を忘備録として載せます。
React Redux 自体は他の方の投稿のほうが詳しく紹介されているので、この記事では connect
を使うときに型を何度も書かない方法と、 connect
されたコンポーネントのテストについて書きます。
Preact Redux でも、型定義のバグが無ければ多分同じように使えます。
React Redux は、 connect
という関数またはデコレータで Redux の state を React の props へ結びつけるライブラリです。
connect
で型の記述を減らす
コンポーネント Container
に対して connect
する場合は、それぞれの方法で以下のように書きます。
(私の手元ではデコレータの方は型がうまくハマりませんでした。 Issue にも挙げられています。)
1 | export default connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(Container); |
TypeScript の場合では、コンポーネントに指定した Property の型の中から mapStateToProps
や mapDispatchToProps
が返すオブジェクトの型のフィールドを抜いた型が外に出ていくことになります。( mergeProps
が定義されていれば、それが返すオブジェクトの型のフィールドが代わりに抜かれる。)
例えば、以下のように書いた場合、これの利用先では fooBar
属性だけ求めるコンポーネントになるということです。
1 | interface Props { |
0 | // in another file |
1 | <Container fooBar={"baz"} /> {/* no `count` attribute */} |
ReturnType<T>
を利用することで、何度も型のフィールドを書かなくても補完や型チェックを行えるようになります。(マッピング関数には大した処理を書かない、かつ、 Container Component が対象になることがほとんどという理由で、明示的に型を定義しなくてもいいと思っています。)
1 | interface PropsToReceive { /* props */ } // if exists |
1 | interface PropsToReceive { /* props */ } // if exists |
(こんなようにするともっと面倒臭さを減らせるが、メンテナンス性からして現実的ではない。)
1 | type FromConnect<T> = T extends InferableComponentEnhancerWithProps<infer P, infer O> ? P : never; // in helper file |
テスト
mapStateToProps
、 mapDispatchToProps
、 mergeProps
をテストする場合には、それぞれに export
をつけるか、 Redux Store のモックを接続して action が dispatch されたか確認してテストすれば良いと思います。ただし、複雑な処理では selector を使うなどすると、マッピングでは定型的なコードが多くなるように思うので、これらマッピング本体をテストする旨味は少ないかもしれませんね。
一方、コンポーネント自体のテストはとても重要です。 connect
されるということは Container Component だと思いますが、子コンポーネントに影響するなど機能が豊富な場合も多いため、テストできると嬉しいはずです。
ストアを準備し connect
後のコンポーネントに対してテストするという結合テストのような格好を取ることもできますが、 connect
される前の状態のコンポーネントも export
し、それに対してテストするほうがバグの発見・予防に役立つのではないかと思います。( react-testing-library の公式サンプルでは前者になっているようですね…)
残念ながら、デコレータのほうの connect
を使うと内部だけを取ってくることができないようなので、後者の戦略をとる場合は関数呼び出しの方の connect
を無難に使ったほうがよさそうです。(型もダメだしテストもだめ。デコレータ版とは…)
そうした場合のテストはこんな感じになります。
1 | import { Container } from "./Container"; // do not use default export for unit test! |
最後に
connect
を使うときに型を何度も書かない方法と、 connect
されたコンポーネントのテストの紹介しました。
React 16.7 からの Hooks ではこのあたりはどう変わるんですかね。丸ごとマップする Hooks か、個別にマップする Hooks か、という選択肢がありそうですが、後者のほうが(自分好みでないものの)しっくりきます。