术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 render 的 prop 共享代码的简单技术 具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑。
更具体地说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。
下面我们一步一步来看看到底什么是 render props
有这样一个需求,我们实现 一个美元汇率,每次增加和减少的时候,转化为欧元的数量也会改变。
https://stackblitz.com/edit/react-pan3gt
import React, { Component } from "react";import "./style.css";const App = () => <Amount />;const Euro = ({ amount }) => <p>Euro: {amount * 0.86}</p>;const Pound = ({ amount }) => <p>Pound: {amount * 0.76}</p>;class Amount extends Component {constructor(props) {super(props);this.state = {amount: 0};}onIncrement = () => {this.setState(state => ({ amount: state.amount + 1 }));};onDecrement = () => {this.setState(state => ({ amount: state.amount - 1 }));};render() {return (<div><span>US Dollar: {this.state.amount} </span><button type="button" onClick={this.onIncrement}>+</button><button type="button" onClick={this.onDecrement}>-</button><Euro amount={this.state.amount} /><Pound amount={this.state.amount} /></div>);}}export default App;
Amount组件来实现增减,Euro和Pound分别来实现欧元和英镑的汇率转化。至此没有什么问题
如果在我们的应用中,想增加人民币汇率,怎么办呢?要找到Amount组件,复制出来一份,然后在Amount中添加人民币的汇率组件,移除Euro和Pound组件。这样做没有问题,但是这样,我们就没有办法实现Amount的组件的复用。
一个想法是,可不可以通过children来实现想要渲染的组件呢?形如:
...render() {return (<div><Amountamount={this.state.amount}onIncrement={this.onIncrement}onDecrement={this.onDecrement}/><Euro amount={this.state.amount} /><Pound amount={this.state.amount} /></div>);}...class Amount extends Component {...render() {return (<div>...{this.props.children} //通过children来动态渲染子组件内容</div>);}}
如果只是单纯的渲染children,子组件(这里指Euro和Pound)就没有办法接收到数量(这里是指按钮增减的数字)的数据。那是不是可以传递函数来动态渲染呢?我们可以把数量作为函数的参数传递给子组件,形如:
const App = () => (<Amount>{() => (<div><Pound amount={amount} /><Euro amount={amount} /></div>)}</Amount>);
看起来可行,那么我们来实现下:
https://stackblitz.com/edit/react-paveqz
import React from "react";import "./style.css";const Euro = ({ amount }) => <p>Euro: {amount * 0.86}</p>;const Pound = ({ amount }) => <p>Pound: {amount * 0.76}</p>;class Amount extends React.Component {constructor(props) {super(props);this.state = {amount: 0};}onIncrement = () => {this.setState(state => ({ amount: state.amount + 1 }));};onDecrement = () => {this.setState(state => ({ amount: state.amount - 1 }));};render() {return (<div><span>US Dollar: {this.state.amount} </span><button type="button" onClick={this.onIncrement}>+</button><button type="button" onClick={this.onDecrement}>-</button>{this.props.children(this.state.amount)} //******</div>);}}const App = () => (<Amount>{amount => ( //*******<div><Pound amount={amount} /><Euro amount={amount} /></div>)} //*******</Amount>);export default App;
36行 Amount组件将数量的state作为入参传递给函数
44行-49行, 函数接收到入参 传递给子组件
这样就实现啦~~就是这么简单 !
如果我们想复用Amount 组件,动态的渲染子组件,那么我们只需要传递不同的渲染子组件就好了,这样就实现了复用!
const App = () => (<Amount>{amount => (<div><h1>My Currency Converter</h1><Rmb amount={amount} /></div>)}</Amount>);
有个疑问,为什么叫Render Props?因为我们之前是使用render这样的一个关键字而不是children的函数,
形如:
const App = () => (<Amountrender={amount => ( ******<div><Pound amount={amount} /><Euro amount={amount} /></div>)}/>);class Amount extends Component {...render() {return (<div><span>US Dollar: {this.state.amount} </span><button type="button" onClick={this.onIncrement}>+</button><button type="button" onClick={this.onDecrement}>-</button>{this.props.render(this.state.amount)}</div>);}}
这种有点像vue的插槽slot的味道,
const App = () => (<AmountrenderAmountOne={amount => ( *******<div><h2>My one Amount</h2><Pound amount={amount} /><Euro amount={amount} /></div>)}renderAmountTwo={amount => ( *******<div><h2>My other Amount</h2><Pound amount={amount} /><Euro amount={amount} /></div>)}/>);class Amount extends Component {...render() {return (<div><span>US Dollar: {this.state.amount} </span>{this.props.renderAmountTwo(this.state.amount)}<button type="button" onClick={this.onIncrement}>+</button><button type="button" onClick={this.onDecrement}>-</button>{this.props.renderAmountOne(this.state.amount)}</div>);}}
完结~~~
当然,关于复用,我们还可以通过HOC来实现呢,参看 High-Order-Component
