术语 “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>
<Amount
amount={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 = () => (
<Amount
render={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 = () => (
<Amount
renderAmountOne={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