术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 render 的 prop 共享代码的简单技术 具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑。

    更具体地说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

    下面我们一步一步来看看到底什么是 render props

    有这样一个需求,我们实现 一个美元汇率,每次增加和减少的时候,转化为欧元的数量也会改变。
    https://stackblitz.com/edit/react-pan3gt
    image.png

    1. import React, { Component } from "react";
    2. import "./style.css";
    3. const App = () => <Amount />;
    4. const Euro = ({ amount }) => <p>Euro: {amount * 0.86}</p>;
    5. const Pound = ({ amount }) => <p>Pound: {amount * 0.76}</p>;
    6. class Amount extends Component {
    7. constructor(props) {
    8. super(props);
    9. this.state = {
    10. amount: 0
    11. };
    12. }
    13. onIncrement = () => {
    14. this.setState(state => ({ amount: state.amount + 1 }));
    15. };
    16. onDecrement = () => {
    17. this.setState(state => ({ amount: state.amount - 1 }));
    18. };
    19. render() {
    20. return (
    21. <div>
    22. <span>US Dollar: {this.state.amount} </span>
    23. <button type="button" onClick={this.onIncrement}>
    24. +
    25. </button>
    26. <button type="button" onClick={this.onDecrement}>
    27. -
    28. </button>
    29. <Euro amount={this.state.amount} />
    30. <Pound amount={this.state.amount} />
    31. </div>
    32. );
    33. }
    34. }
    35. export default App;

    Amount组件来实现增减,Euro和Pound分别来实现欧元和英镑的汇率转化。至此没有什么问题
    如果在我们的应用中,想增加人民币汇率,怎么办呢?要找到Amount组件,复制出来一份,然后在Amount中添加人民币的汇率组件,移除Euro和Pound组件。这样做没有问题,但是这样,我们就没有办法实现Amount的组件的复用。

    一个想法是,可不可以通过children来实现想要渲染的组件呢?形如:

    1. ...
    2. render() {
    3. return (
    4. <div>
    5. <Amount
    6. amount={this.state.amount}
    7. onIncrement={this.onIncrement}
    8. onDecrement={this.onDecrement}
    9. />
    10. <Euro amount={this.state.amount} />
    11. <Pound amount={this.state.amount} />
    12. </div>
    13. );
    14. }
    15. ...
    16. class Amount extends Component {
    17. ...
    18. render() {
    19. return (
    20. <div>
    21. ...
    22. {this.props.children} //通过children来动态渲染子组件内容
    23. </div>
    24. );
    25. }
    26. }

    如果只是单纯的渲染children,子组件(这里指Euro和Pound)就没有办法接收到数量(这里是指按钮增减的数字)的数据。那是不是可以传递函数来动态渲染呢?我们可以把数量作为函数的参数传递给子组件,形如:

    1. const App = () => (
    2. <Amount>
    3. {() => (
    4. <div>
    5. <Pound amount={amount} />
    6. <Euro amount={amount} />
    7. </div>
    8. )}
    9. </Amount>
    10. );

    看起来可行,那么我们来实现下:
    https://stackblitz.com/edit/react-paveqz

    1. import React from "react";
    2. import "./style.css";
    3. const Euro = ({ amount }) => <p>Euro: {amount * 0.86}</p>;
    4. const Pound = ({ amount }) => <p>Pound: {amount * 0.76}</p>;
    5. class Amount extends React.Component {
    6. constructor(props) {
    7. super(props);
    8. this.state = {
    9. amount: 0
    10. };
    11. }
    12. onIncrement = () => {
    13. this.setState(state => ({ amount: state.amount + 1 }));
    14. };
    15. onDecrement = () => {
    16. this.setState(state => ({ amount: state.amount - 1 }));
    17. };
    18. render() {
    19. return (
    20. <div>
    21. <span>US Dollar: {this.state.amount} </span>
    22. <button type="button" onClick={this.onIncrement}>
    23. +
    24. </button>
    25. <button type="button" onClick={this.onDecrement}>
    26. -
    27. </button>
    28. {this.props.children(this.state.amount)} //******
    29. </div>
    30. );
    31. }
    32. }
    33. const App = () => (
    34. <Amount>
    35. {amount => ( //*******
    36. <div>
    37. <Pound amount={amount} />
    38. <Euro amount={amount} />
    39. </div>
    40. )} //*******
    41. </Amount>
    42. );
    43. export default App;

    36行 Amount组件将数量的state作为入参传递给函数
    44行-49行, 函数接收到入参 传递给子组件
    这样就实现啦~~就是这么简单 !
    如果我们想复用Amount 组件,动态的渲染子组件,那么我们只需要传递不同的渲染子组件就好了,这样就实现了复用!

    1. const App = () => (
    2. <Amount>
    3. {amount => (
    4. <div>
    5. <h1>My Currency Converter</h1>
    6. <Rmb amount={amount} />
    7. </div>
    8. )}
    9. </Amount>
    10. );

    有个疑问,为什么叫Render Props?因为我们之前是使用render这样的一个关键字而不是children的函数,
    形如:

    1. const App = () => (
    2. <Amount
    3. render={amount => ( ******
    4. <div>
    5. <Pound amount={amount} />
    6. <Euro amount={amount} />
    7. </div>
    8. )}
    9. />
    10. );
    11. class Amount extends Component {
    12. ...
    13. render() {
    14. return (
    15. <div>
    16. <span>US Dollar: {this.state.amount} </span>
    17. <button type="button" onClick={this.onIncrement}>
    18. +
    19. </button>
    20. <button type="button" onClick={this.onDecrement}>
    21. -
    22. </button>
    23. {this.props.render(this.state.amount)}
    24. </div>
    25. );
    26. }
    27. }

    这种有点像vue的插槽slot的味道,

    1. const App = () => (
    2. <Amount
    3. renderAmountOne={amount => ( *******
    4. <div>
    5. <h2>My one Amount</h2>
    6. <Pound amount={amount} />
    7. <Euro amount={amount} />
    8. </div>
    9. )}
    10. renderAmountTwo={amount => ( *******
    11. <div>
    12. <h2>My other Amount</h2>
    13. <Pound amount={amount} />
    14. <Euro amount={amount} />
    15. </div>
    16. )}
    17. />
    18. );
    19. class Amount extends Component {
    20. ...
    21. render() {
    22. return (
    23. <div>
    24. <span>US Dollar: {this.state.amount} </span>
    25. {this.props.renderAmountTwo(this.state.amount)}
    26. <button type="button" onClick={this.onIncrement}>
    27. +
    28. </button>
    29. <button type="button" onClick={this.onDecrement}>
    30. -
    31. </button>
    32. {this.props.renderAmountOne(this.state.amount)}
    33. </div>
    34. );
    35. }
    36. }

    完结~~~

    当然,关于复用,我们还可以通过HOC来实现呢,参看 High-Order-Component

    Reference:
    https://www.robinwieruch.de/react-render-props