react-reduxのconnect関数は、いろいろな書き方ができることで知られています。
(参考:ReactとReduxを結ぶパッケージ「react-redux」についてconnectの実装パターンを試す)
その柔軟さゆえ、実際にアプリケーションの中で書く場合にどのパターンを選ぶか悩むことが多いのではないでしょうか。今回は自分の気に入っている書き方を紹介します。
最もシンプルな書き方
import { connect } from 'react-redux'
import MyComponent from './my-component'
import { actionCreatorA, actionCreatorB } from './my-module'
export default connect(
({users, todos}) => ({users, todos}),
{
actionCreatorA,
actionCreatorB
}
)(MyComponent)
これだけです。stateを特にいじらずにconnectしたいときはこれで十分です。action creatorsは、rest spread operatorでまるっと渡してしまっても良いです。
reselectとか使うなら
import { connect } from 'react-redux'
import MyComponent from './my-component'
import { actionCreatorA, actionCreatorB } from './my-module'
import { mySelector } from './selectors'
export default connect(
state => ({
todos: mySelector(state)
}),
{
actionCreatorA,
actionCreatorB
}
)(MyComponent)
reselectを使ったり、stateのconnectの仕方に工夫がいる場合はこのように書きます。
actionCreatorsをactionsでまとめたい時
import { connect } from 'react-redux'
import MyComponent from './my-component'
import { actionCreatorA, actionCreatorB } from './my-module'
export default connect(
({users, todos}) => ({users, todos}),
dispatch => ({
actions: {
actionCreatorA: () => dispatch(actionCreatorA()),
actionCreatorB: () => dispatch(actionCreatorB())
}
})
)(MyComponent)
よくある書き方と比べると
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import MyComponent from './my-component'
import { actionCreatorA, actionCreatorB } from './my-module'
function mapStateToProps(state) {
return {
users: state.users
todos: state.todos
}
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
やっていることは紹介したシンプルな書き方と同じことですが、かなり冗長に見えます。
解説
キモは、mapStateToProps、mapDispatchToPropsを定義していないところです。特に再利用するものでもないですし、直接書いたほうが読みやすいと思います。
mapSteteToProps
connectの第一引数、即ちmapStateToPropsは、stateを引数に取り、Objectを返すただの関数です。
これは、stateをDestructuringしてアロー関数にしてconnectに直接渡してあげればシンプルに書けるということですね。
mapDispatchToProps
これはややこしいです。そもそも bindActionCreators
はどこに行ったのでしょうか。
bindActionCreators
の役割は、Store#dispatchで関数をラップすることです。簡易的に書くと、以下のようなobjectを返してくれます。
{
actionCreatorA: () => {
dispatch(actionCreatorA())
},
actionCreatorB: (foo) => {
dispatch(actionCreatorB(foo))
}
}
こうすることで、Store#dispatchを書かずに直接呼び出せるということです。
実はconnect関数も同じことをやってくれます。 正確に言うと、connect関数の第2引数は関数かobjectを取ることができて、オブジェクトが渡された場合はbindActionCreatorsと同じように、object内の各関数をdispatchでラップしてくれます。
この特性を利用すると、紹介したような書き方が実現できます。
export default connect(
state => ({
todos: mySelector(state)
}),
{
actionCreatorA, // dipatchでラップされる
actionCreatorB // dispatchでラップされる
}
)(MyComponent)
以上です。
おかしなところがあればご指摘いただければと思いますmm