Reactガイドを読んでいくその96
これは
Reactのガイドを読んでいく記事です。
ガイドのリンク
レンダープロップ
横断的関心事にレンダープロップを使う
コンポーネントはReactでコードを再利用するための主な要素になるが、コンポーネントがカプセル化したstateや振る舞いを、同じstateを必要とする別のコンポーネントと共有する方法についてはあまり明らかになっていない。
ドキュメント同様以下のコードを参考に考えていく。
以下のコードはマウスの位置を追跡するもので、画面上でカーソル移動させるとコンポーネントがx,y座標を
内に表示する。
class MouseTracker extends React.Component { constructor(props) { super(props); this.handleMouseMove = this.handleMouseMove.bind(this); this.state = { x: 0, y: 0 }; } handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <h1>Move the mouse around!</h1> <p>The current mouse position is ({this.state.x}, {this.state.y})</p> </div> ); } }
他のコンポーネントでもこのロジックをコンポーネント化して、使いまわすことはできるのか?
というのがここで考えたいことになる。
上記のコードを少しリファクタリングして、必要な機能をカプセル化したものを作ってみる。
class Mouse extends React.Component { constructor(props) { super(props); this.handleMouseMove = this.handleMouseMove.bind(this); this.state = { x: 0, y: 0 }; } handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> {/* ...but how do we render something other than a <p>? */} <p>The current mouse position is ({this.state.x}, {this.state.y})</p> </div> ); } } class MouseTracker extends React.Component { render() { return ( <div> <h1>Move the mouse around!</h1> <Mouse /> </div> ); } }
もう少し改良を加えてみる
class Cat extends React.Component { render() { const mouse = this.props.mouse; return ( <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} /> ); } } class MouseWithCat extends React.Component { constructor(props) { super(props); this.handleMouseMove = this.handleMouseMove.bind(this); this.state = { x: 0, y: 0 }; } handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> {/* We could just swap out the <p> for a <Cat> here ... but then we would need to create a separate <MouseWithSomethingElse> component every time we need to use it, so <MouseWithCat> isn't really reusable yet. */} <Cat mouse={this.state} /> </div> ); } } class MouseTracker extends React.Component { render() { return ( <div> <h1>Move the mouse around!</h1> <MouseWithCat /> </div> ); } }
ここでレンダープロップを使って実装していく。
class Cat extends React.Component { render() { const mouse = this.props.mouse; return ( <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} /> ); } } class Mouse extends React.Component { constructor(props) { super(props); this.handleMouseMove = this.handleMouseMove.bind(this); this.state = { x: 0, y: 0 }; } handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> {/* Instead of providing a static representation of what <Mouse> renders, use the `render` prop to dynamically determine what to render. */} {this.props.render(this.state)} </div> ); } } class MouseTracker extends React.Component { render() { return ( <div> <h1>Move the mouse around!</h1> <Mouse render={mouse => ( <Cat mouse={mouse} /> )}/> </div> ); } }
今日はここまで。