React 关于render props技术的理解

736 阅读4分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」。

render props是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的技术。

简单来说,给一个组件传入一个prop,这个props是一个函数,函数的作用是用来告诉这个组件需要渲染什么内容,那么这个prop就成为render prop

关于render props的使用场景,我们先来看一段代码:

// A组件
class A extends Component {
    state = { count: 0 };
    add = () => {
        this.setState({ count: this.state.count + 1 });
    };
    render() {
        return (
            <div className="a">
                <h2>A组件</h2>
                <button onClick={this.add}>add</button>
                <B count={this.state.count} />
            </div>
        );
    }
}

// B组件
class B extends Component {
    render() {
        return <h3 className="b">B组件,{this.props.count}</h3>;
    }
}

// Father组件
export default class Father extends Component {
    render() {
        return (
            <div className="father">
                <h2>Father组件</h2>
                <A />
            </div>
        );
    }
}

最外层是一个Father组件,Father组件里面有个A组件作为子组件。在A组件的state中,定义了count计数值,并通过点击按钮来增加计数值。同时,在A组件中,又嵌套了一个B组件作为子组件,将A组件中的count计数值传给B组件。B组件通过props接收到传来的count,展示到页面上。各组件关系以及运行效果如下图所示:

以上代码虽然实现了效果:在A组件中控制count,在B组件中展示count值。但是,这种A组件和B组件的嵌套关系是固定的。

假如我们不希望B作为A的子组件,希望另一个C组件来作为A的子组件显示count;或者,希望有另一个A,但是这个A里面的子组件不是B而是组件C。这时候,我们就需要修改A组件中的代码,将B组件换成C组件,这显然不够高效,不够优雅。A组件中的state、控制statecount计数值的代码逻辑,都是相同的,我们却没有复用。

render props解决了这类问题,能够封装固定的逻辑,动态地确定需要渲染的内容。可以这么理解:A组件中的state、控制statecount计数值的代码逻辑是不变的,改变的只是谁作为A的子组件,有可能是B,也有可能是C。那么我们就可以在需要渲染子组件的地方,留一个空位,将来可以选择渲染B,也可以选择渲染C。这样,就复用了不变的逻辑,又能动态地选择要渲染的内容。

具体如何实现?来看如下一段的代码:

// A组件
class A extends Component {
    state = { count: 0 };
    add = () => {
        this.setState({ count: this.state.count + 1 });
    };
    render() {
        return (
            <div className="a">
                <h2>A组件</h2>
                <button onClick={this.add}>add</button>
                {this.props.render()}
            </div>
        );
    }
}

// Father组件
export default class Father extends Component {
    render() {
        return (
            <div className="father">
                <h2>Father组件</h2>
                <A render={() => <h3>h3标签</h3>} />
            </div>
        );
    }
}

Father组件中,A组件作为子组件,给A组件传入了一个prop,名为render,类型是一个函数,函数的作用就是返回<h3>标签。在A组件中,通过this.props.render拿到了传来的render,但这只是一个函数,只用调用这个函数,才能获得返回值,所以通过{this.props.render()}调用了传入的函数,返回一个<h3>标签。这样,<h3>标签就被渲染到页面上了。

如上代码,A组件的{this.props.render()}就像是一个预留的空位,它具体是什么,取决于A的父组件给A传什么。这样,A组件关于count的逻辑不用改,只需更改给A传的render即可,A组件动态渲染需要渲染的内容。

将上述用例用render props修改完整:

// A组件
class A extends Component {
    state = { count: 0 };
    add = () => {
        this.setState({ count: this.state.count + 1 });
    };
    render() {
        return (
            <div className="a">
                <h2>A组件</h2>
                <button onClick={this.add}>add</button>
                {this.props.render(this.state.count)}
            </div>
        );
    }
}

// B组件
class B extends Component {
    render() {
        return <h3 className="b">B组件,{this.props.count}</h3>;
    }
}

// Father组件
export default class Father extends Component {
    render() {
        return (
            <div className="father">
                <h2>Father组件</h2>
                <A render={count => <B count={count} />} />
            </div>
        );
    }
}

Father组件给A组件传入了一个prop,名为render,类型是个函数。函数接收一个count参数,返回B组件,同时给B组件传入了countA组件内,通过{this.props.render(this.state.count)}预留了一个空位,将state内部的count作为函数的参数,调用这个传来的函数。B组件通过this.props.count拿到了传来的count,展示到了页面上。

若不希望用B组件来展示,想用C组件,那么只需在Father组件中,修改A组件的render即可:

<A render={count => <C count={count} />} />


以上是本人学习所得之拙见,若有不妥,欢迎指出交流!