この記事は Meguro.es #17 のプレゼン資料です。SpeackerDeck に PDF をアップしましたが、ドキュメントのメンテは Qiita で行います。
目的
- render-props とは何かを理解すること
- Function as child とは何かを理解すること
- render-props を使ったコードが読めるようになること
- Function as child を使ったコードが読めるようになること
省略すること
- SFC。すべてクラス構文に統一しました。
- 「値」、「参照」といった用語の厳密な使い方
- props の型
コンポーネント呼び出しの素朴な例
親コンポーネントが子コンポーネントを呼び出す、もっとも単純な例です。
class Parent extends Component {
render() {
return <Child />
}
}
class Child extends Component {
render() {
return '子コンポーネント'
}
}
親から子へ文字列を渡す
親コンポーネントは子コンポーネントに、JSX の属性として、文字列を渡すことができます。
JSX の属性
として指定することで、this.props
でそれを参照することができます。
import React, { Component } from 'react'
class Parent extends Component {
render() {
const something = 'aaa'
return <Child something={something} />
}
}
class Child extends Component {
render() {
return <>something は {this.props.something}</>
}
}
親から子へオブジェクトを渡す
親コンポーネントは子コンポーネントに、JSX の属性として、オブジェクトを渡すことができます。たとえば、{ keyA: 'aaa'}
です。
class Parent extends Component {
render() {
const something = { keyA: 'aaa'}
return <Child something={something} />
}
}
class Child extends Component {
render() {
return <>something.keyA は {this.props.something.keyA}</>
}
}
親から子へ関数を渡す
親コンポーネントは子コンポーネントに、JSX の属性として、関数を渡すことができます。
JSX の属性が関数なら、コールバックとして使えるということです。
class Parent extends Component {
something() {
console.log('aaaaaa')
}
render() {
return <Child something={this.something} />
}
}
class Child extends Component {
render() {
this.props.something()
return <>子コンポーネント</>
}
}
親から子へコンポーネントを返す関数を渡す
先程の例では、JSX の属性として渡した関数は、console.log を実行しただけでした。
コンポーネント(React.Element) を返すという使い方がもっと有益です。
たとえば、親コンポーネントで DOM 要素を返す関数を定義して、子コンポーネントの props として渡します。
そして、子コンポーネントの render メソッドの定義中でその関数を実行します。
次の例では、prop の名前を something から render に変更し、雰囲気をだしました。
class Parent extends Component {
something() {
return <>親コンポーネント</>
}
render() {
return <Child render={this.something} />
}
}
class Child extends Component {
render() {
return this.props.render()
}
}
親から子へコンポーネントを渡す1
親コンポーネントは子コンポーネントに、JSX の属性として、コンポーネントを渡すことができます。
class WanderingComponents extends Component {
render() {
return <>WanderingComponents</>
}
}
class Parent extends Component {
render() {
return <Child myComponent={<WanderingComponents />} />
}
}
class Child extends Component {
render() {
return <>{this.props.myComponent}</>
}
}
親から子へコンポーネントを渡す2
先程の例は、JSX の属性名を children にすると、Child コンポーネントはよくみるイディオムになります。
class WanderingComponents extends Component {
render() {
return <>WanderingComponents</>
}
}
class Parent extends Component {
render() {
return <Child children={<WanderingComponents />} />
}
}
class Child extends Component {
render() {
return <>{this.props.children}</>
}
}
コンポーネントのネストと children 属性
親から子にコンポーネントを渡すとき、JSX の属性としてではなく、コンポーネントをネストさせる形で渡すこともできます。ネストさせると呼び出し側から属性名の記述が消えますが、children
という名前で受け取れると規定されています。
以下の書き方で、WanderingComponents は Child の子コンポーネントになるので、Child では this.props.children で WanderingComponents を取得できます。
class WanderingComponents extends Component {
render() {
return <>WanderingComponents</>
}
}
class Parent extends Component {
render() {
return (
<Child>
<WanderingComponents />
</Child>
)
}
}
class Child extends Component {
render() {
return this.props.children
}
}
Function as child パターン
コンポーネントのネスト部が children 属性に対応するということを確認しました。
このコンポーネントのネスト部は、コンポーネントそのものだけではなく関数にすることもできます。
children 属性がコールバックとして使えるということです。
その場合、render での children 属性の呼び出しは {this.props.children}
ではなく {this.props.children()}
です。
これを「Function as child パターン」と呼びます。
class Parent extends Component {
something() {
return <>something</>
}
render() {
return (
<Child>
{this.something}
</Child>
)
}
}
class Child extends Component {
render() {
return this.props.children() // () をつけて関数を実行した
}
}
Functions as Children と render-props の関係
一つ前の子コンポーネントの reunder メソッドは return this.props.children()
という定義でした。
children 属性が関数である必要があると読み取れます。
これは JSX の属性として関数を渡す、いわゆる render-props です。
コンポーネントを返す関数を prop とする JSX の属性名を render から children に変更してみましょう。
render 版
import React, { Component } from 'react'
class Parent extends Component {
something() {
return <>something</>
}
render() {
return <Child render={this.something} />
}
}
class Child extends Component {
render() {
return this.props.render()
}
}
children 版
import React, { Component } from 'react'
class Parent extends Component {
something() {
return <>something</>
}
render() {
return <Child children={this.something} />
}
}
class Child extends Component {
render() {
return this.props.children()
}
}
挙動は同一です。
Functions as Children は render-props の一種だとみなせることがわかりました。