こんにちは。
質問1:
ハッシュであるexpenseを分割代入して子供に渡しているのはわかるのですが、dispatchというメソッドはそのハッシュの中にはないはずなのに、なぜ分割代入の要領で、親から子に渡すことができるのでしょうか。
ExpenseListItem
に props経由で dispatch
が渡されてくる理由は、
javascript
1export default connect()(ExpenseListItem);
としているからです。
この connect
の書き方は、react-redux の公式ドキュメントにある connect() の使い方の事例の一番上の例
・Inject just dispatch and don't listen to store
javascript
1export default connect()(TodoApp)
と同じように、mapStateToProps
, mapDispatchToProps
を与えない使い方です。この場合、redux からprops経由で渡されるものは、(ドキュメントに Inject just dispatch
とあるとおり、) dispatch
だけです。
connect
による効果によって dispatch
が props に渡されることは、以下のようにすれば確かめられます。
javascript
1export default connect()(ExpenseListItem);
を、以下のように単にExpenseListItem
をそのまま export するように書き換えます。
javascript
1export default ExpenseListItem;
上記のようにしてから、ExpenseList を画面に表示させ、各ExpenseListItemの [remove]ボタンをクリックしてみると、アクションが発行されることはなく、おそらく dispatch is not a function
というエラーメッセージがコンソールに表示されると思います。
質問2:
この分割代入の要領で子供に渡すことができるのはdispatchのみですか?他のメソッドも渡すことができるのでしょうか
渡せます。
以下、その一例です。
javascript
1export default connect()(ExpenseListItem);
としているところを
javascript
1const mapDispatchToProps = dispatch => ({
2 remove: id => { dispatch(removeExpense({id})); }
3});
4
5export default connect(null, mapDispatchToProps)(ExpenseListItem);
とすると、上記の remove
が props 経由で渡されるので、ExpenseListItem
は以下のようになります。
jsx
1import React from 'react';
2import { connect } from 'react-redux';
3import { removeExpense } from '../actions/expenses';
4
5const ExpenseListItem = ({ remove, id, description, amount, createdAt }) => (
6 <div>
7 <h1>{description}</h1>
8 <p>{amount}-{createdAt}</p>
9 <button onClick={() => {
10 remove(id)
11 }}>
12 remove
13 </button>
14 </div>
15)
16
次に、 connect
の働きを把握するために HOC
という用語を説明します。
javascript
1export default connect()(ExpenseListItem);
の connect()
は関数を返します。どのような関数かというと、コンポーネントを受け取ってコンポーネントを返すような関数です。そのような関数のことを一般には Higher-Order Components (HOC)といいます。connect()
はHOCを返しますが、どのようなことを提供してくれるHOCかといえば、引数で指定されたコンポーネント(上記では ExpenseListItem
)に 「props 経由で dispatch が渡されてくる」という機能を追加したコンポーネントを返します。これを踏まえたうえで、
dispatchのみですか?
への回答として、上記のように、あるコンポーネントを、特別な用途に作られたHOCに渡して、機能拡張されたコンポーネントを得るという、別の例を挙げます。
画面のURLとコンポーネントをマッピングするためによく使用される react-router で提供される withRouter がそのようなHOCの例です。
withRouter
の働きは、A
というコンポーネントがあったときに、withRouter(A)
が返すコンポーネントを適切にルーティング設定すると、A
には、match
, location
, history
の3点がprops経由で渡されますので、これらを使ってURLや画面遷移の操作等を実現できます。(ただしこれら3つのpropsは関数ではありません。connect()
が返すHOC以外にも、Reactによる開発でよく使う HOCの事例として withRouter
を挙げました。)
また、 HOCは自作することもできます。任意の名前、例えば何らかの x
というprops を挿入するようなwithX
という HOCを作っておいて、あるコンポーネント A
でこの withX
からの this.props.x
を使いたいのであれば、A
のソースコードでexport するところを
javascript
1export default withX(A);
とすればよいです。
このようにすると、 A
を使う側で、例えば <A />
のように何もpropsを渡さないとしても、A
の中では withX
が渡してくれる想定のthis.props.x
を何らかの用途に使うことができます。 props x
を提供することで、何を A
に機能追加したいのかは、withX
で詳細を実装します。
以上、参考になれば幸いです。
追記
もし仮にmapStateToPropsを”与える”やり方で書いた場合においても、dispatchはpropsに渡されますか?
渡されます。
仮に redux state に、システムからのお知らせとして、 messages というプロパティがあるとします。これを ExpenseListItem
で使いたいとき、 mapStateToProps
を
javascript
1const mapStateToProps = state => ({
2 messages: state.messages
3});
としておいて、connect を
javascript
1export default connect(mapStateToProps)(ExpenseListItem);
と、mapStateToProps
は指定するけれども mapDispatchToProps
は指定しない書き方をすると、connect によってprops に渡されるのは messages
と dispatch
の2点ということになります。