【React】使用渲染属性(render props)实现代码复用

160 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

本文主要内容:介绍了render props的运用,使用render props代替HOC实现了代码复用

昨天写了一篇关于高阶组件转换原有组件实现代码复用的文章,今天随着课程进度的推进,了解到了render props这种更适合代码复用的办法,在此做一些总结介绍

名词解释

render props,在编写的实践理解后,就是将本组件的内容作为公用组件的函数props的返回值,然后使用公用组件提供的数据、函数这一类可复用的资源,实现同样或者类似的功能,也是相当于对本组件进行了一个加工。

props传入文本

所以我们先从基础的不是函数的props看起,下面是来自组件Example的代码,可以看到接收了props,并且将props中的name属性渲染了出来

import React from "react"

function Example(props) {
    return <h1>Hi, {props.name}</h1>
}

export default Example

因此App.js中的代码就如下

function App() {
    return (
        <div>
            <Example name={"Bob"} />
        </div>
    )
}

向props中传入了name属性,能获得下面的结果

image.png

props传入函数

传入一个文本已经是非常简单的事情了,但是如果我们传入的是一个函数呢

所以用<Example name={function() {return ("Bob")}} />传入一个函数

执行没有报错,但是浏览器的调试工具给我们报了错误

image.png

可以知道函数是不能被直接渲染出来的,我们应该接收的是函数的返回值

所以修改Example组件中的return <h1>Hi, {props.name}</h1>return <h1>Hi, {props.name()}</h1>,添加上括号表示函数的调用,这样我们就得到了同样的效果

现在我们删掉Example组件中的h1,直接用div包裹props,如下

function Example(props) {
    return (
        <div>
            {props.name()}
        </div>
    )
}

这时我们可以通过函数返回一个<h1>包裹的元素获得同样的效果,即使用如下代码

<Example name={function() {return (<h1>Hi, Bob</h1>)}} />

接下来我们要做的事可能有点难以理解。既然传入的是函数,那么它应该可以接受参数,所以我们在Example组件中,使用{props.name("Bob")}向函数中传入参数

然后在App.js的组件传入的函数props中接收props,如下

<Example name={function(props) {return <h1>Hi, {props}</h1>}} />

再给他们改个名,调整一下缩进,App组件中的Example如下

<Example render={
    function(name) {
        return <h1>Hi, {name}</h1>
    }
} />

所以Example组件中接收的props也要修改为render,{props.render("Bob")}

这样我们就通过Example组件向App组件传递了数据,也就是丰富了App组件的内容、功能。

例子说明

仅仅通过上面最简单的演示应该还是很难理解,接下来就用昨天的例子来实现一下render props完成的代码复用

还是以Favorite为例,

获得Toggler组件

我们需要在Favorite中渲染Toggler组件,所以首先就是将withToggler.js转换为Toggler组件,也就是将原本输出的函数

export function withToggler(component) {
    return function(props) {
        return (
            <Toggler component={component} {...props}/>
        )
    }
}

修改为输出组件export default Toggler

引入Toggler组件

第二步需要引入Toggler组件,并且将Favorite中原本的内容移入到props函数中,如下

function Favorite(props) {
    return (
        <Toggler render={function() {
            return(
                <div>
                    <h3>Click heart to favorite</h3>
                    <h1>
                        <span 
                            onClick={props.toggle}
                        >
                            {props.on ? "❤️" : "♡"}
                        </span>
                    </h1>
                </div>
            )
        }} />
    ) 
}

这时还需要将这个props回到Toggler中渲染出来,所以修改Toggler.js中render()代码如下

render() {
    return (
       <div>
            {/* {this.props.render()} */}
       </div>
    )
}

Toggler中传回数据与函数

接着我们要让Toggler提供state和toggle(),所以要在render()中传入,如下

render() {
    return (
       <div>
            {this.props.render(this.state.on, this.toggle)}
       </div>
    )
}

可以看到通过this传回了两个需要的参数

Favorite使用传来的数据和函数

这时我们就可以在Favorite组件中接收了,使用如下代码

<Toggler render={function(on, toggle) {
            return(
             ......
            )
        }} />

分别用on和toggle接收了state和函数

至于使用则是删掉原本的props,直接用on和toggle就可以了,如下

<Toggler render={function(on, toggle) {
    return(
        <div>
            <h3>Click heart to favorite</h3>
            <h1>
                <span 
                    onClick={toggle}
                >
                    {on ? "❤️" : "♡"}
                </span>
            </h1>
        </div>
    )
}} />

所以同理可以使用箭头函数也对Menu进行类似修改

function Menu() {
    return (
        <Toggler render={
            (on, toggle) => (
                <div>
                    <button onClick={toggle}>{on ? "Hide" : "Show"} Menu </button>
                    <nav style={{display: on ? "block" : "none"}}>
                        <h6>Signed in as Coder123</h6>
                        <p><a>Your Profile</a></p>
                        <p><a>Your Repositories</a></p>
                        <p><a>Your Stars</a></p>
                        <p><a>Your Gists</a></p>
                    </nav>
                </div>
            )
        } />
    )
}

这样我们就用render props对HOCs进行了修改,同样实现了代码复用