最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React - Render Props

    正文概述 掘金(一个前端王)   2021-07-10   360

    本文是对React官网Render Props的学习总结。

    Render Props

    Render Props是指 React 组件之间使用一个值作为函数的 prop 共享代码的简单技术。

    具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑。

    <DataProvider 
        render={ data => (<h1>Hello {data.target}</h1>) }
    />
    

    使用Render Props 解决横切关注点(Cross-Cutting Concerns)

    组件是 React 代码复用的主要单元,但如何将一个组件封装的状态或行为共享给其他需要相同状态的组件并不总是显而易见。

    例如,以下组件跟踪 Web 应用程序中的鼠标位置:

    class MouseTracker extends React.Component {
        constructor(props) {
            super(props);
            this.handleMouseMove = this.handleMouseMove.bind(this);
            this.state = { x: 0, y: 0 };
        }
        
        handleMouseMove(event) {
            this.setState({
                x: event.clientX,
                y: event.clientY
            });
        }
        render() {
            return (
                <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
                    <h1>移动鼠标!</h1>
                    <p>当前的鼠标位置是 ({this.state.x}, {this.state.y})</p>
                </div>
            );
        }
    }
    

    当光标在屏幕上移动时,组件在 < p > 中显示其(x,y)坐标。

    如果此时,其他组件也想获取(x, y)坐标,就需要把这一部分代码逻辑封装起来。 可以试着在 Mouse 组件中封装我们需要共享的行为。

    class Mouse extends React.Component {
        constructor(props) {
            super(props);
            this.handleMouseMove = this.handleMouseMove.bind(this);
            this.state = { x: 0, y: 0 };
        }
        handleMouseMove(event) {
            this.setState({
                x: event.clientX,
                y: event.clientY
            });
        }
        render() {
            return (
                <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
    
                    {/* ...但我们如何渲染 <p> 以外的东西? */}
                    <p>The current mouse position is ({this.state.x}, {this.state.y})</p>
                </div>
            );
        }
    }
    
    class MouseTracker extends React.Component {
        render() {
            return (
                <>
                    <h1>移动鼠标!</h1>
                    <Mouse />
                </>
            );
        }
    }
    

    现在 < Mouse > 组件封装了所有关于监听 mousemove 事件和存储鼠标 (x, y) 位置的行为,但其仍不是真正的可复用。

    举个例子,假设我们有一个 < Cat > 组件,它可以呈现一张在屏幕上追逐鼠标的猫的图片。我们或许会使用 < Cat mouse = {{ x, y }} /> 通过 prop 来告诉组件鼠标的坐标以让它知道图片应该在屏幕哪个位置。

    首先, 尝试在 < Mouse > 内部的渲染方法渲染 < Cat > 组件:

    class Cat extends React.Component {
        render() {
            const mouse = this.props.mouse;
            return (
                <img 
                    src="/cat.jpg" 
                    style={{ position: 'absolute', left: mouse.x, top: mouse.y }} 
                />
            );
        }
    }
    
    class MouseWithCat extends React.Component {
        constructor(props) {
            super(props);
            this.handleMouseMove = this.handleMouseMove.bind(this);
            this.state = { x: 0, y: 0 };
        }
        handleMouseMove(event) {
            this.setState({
                x: event.clientX,
                y: event.clientY
            });
        }
        render() {
            return (
                <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
                    {/*
                        我们可以在这里换掉 <p> 的 <Cat>   ......
                        但是接着我们需要创建一个单独的 <MouseWithSomethingElse>
                        每次我们需要使用它时,<MouseWithCat> 是不是真的可以重复使用.
                    */}
                    <Cat mouse={this.state} />
                </div>
            );
        }
    }
    
    class MouseTracker extends React.Component {
        render() {
            return (
                <div>
                    <h1>移动鼠标!</h1>
                    <MouseWithCat />
                </div>
            );
        }
    }
    

    这种方法仅仅适用于我们的特定用例,但我们还没有达到以可复用的方式真正封装行为的目标。现在,每当我们想要鼠标位置用于不同的用例时,我们必须创建一个新的组件(本质上是另一个 < MouseWithCat > ),它专门为该用例呈现一些东西。

    这也是 render prop 的来历:相比于直接将 < Cat > 写死在 < Mouse > 组件中,并且有效地更改渲染的结果,我们可以为 < Mouse > 提供一个函数 prop 来动态的确定要渲染什么 —— 一个 render prop。

    class Cat extends React.Component {
        render() {
            const mouse = this.props.mouse;
            return (
                <img 
                    src="/cat.jpg" 
                    style={{ position: 'absolute', left: mouse.x, top: mouse.y }}
                />
            );
        }
    }
    
    class Mouse extends React.Component {
        constructor(props) {
            super(props);
            this.handleMouseMove = this.handleMouseMove.bind(this);
            this.state = { x: 0, y: 0 };
        }
        handleMouseMove(event) {
            this.setState({
                x: event.clientX,
                y: event.clientY
            });
        }
        render() {
            return (
                <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
    
                    {/*
                        使用 render prop 动态决定要渲染的内容,
                        而不是给出一个 <Mouse> 渲染结果的静态表示
                    */}
                    {this.props.render(this.state)}
                </div>
            );
        }
    }
    
    class MouseTracker extends React.Component {
        render() {
            return (
                <div>
                    <h1>移动鼠标!</h1>
                    <Mouse render={mouse => (
                        <Cat mouse={mouse} />
                    )}/>
                </div>
            );
        }
    }
    

    我们提供了一个 render 方法 让 < Mouse > 能够动态决定什么需要渲染,而不是克隆 < Mouse > 组件然后硬编码来解决特定的用例。

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

    这项技术使我们共享行为非常容易。要获得这个行为,只要渲染一个带有 render prop 的 < Mouse > 组件就能够告诉它当前鼠标坐标 (x, y) 要渲染什么。

    关于 render prop 一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数高阶组件 (HOC)。 例如,如果你更喜欢使用 withMouse HOC 而不是 < Mouse > 组件,你可以使用带有 render prop 的常规 < Mouse > 轻松创建一个:

    // 如果你出于某种原因真的想要 HOC,那么你可以轻松实现
    // 使用具有 render prop 的普通组件创建一个!
    function withMouse(Component) {
        return class extends React.Component {
            render() {
                return (
                    <Mouse render={mouse => (
                        <Component {...this.props} mouse={mouse} />
                    )}/>
                );
            }
        }
    }
    

    使用 Props 而非 render

    重要的是要记住,render prop 是因为模式才被称为 render prop,你不一定要用名为 render 的 prop 来使用这种模式。事实上,任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”。

    尽管之前的例子使用了 render,我们也可以简单地使用 children prop。

    <Mouse children={mouse => (
        <p>鼠标的位置是 {mouse.x},{mouse.y}</p>
    )}/>
    

    children prop 并不真正需要添加到 JSX 元素的 “attributes” 列表中。相反,可以直接放置到元素的内部。

    <Mouse>
        {mouse => (
            <p>鼠标的位置是 {mouse.x},{mouse.y}</p>
        )}
    </Mouse>
    

    由于这一技术的特殊性,当你在设计一个类似的 API 时,你或许会要直接地在你的 propTypes 里声明 children 的类型应为一个函数。

    Mouse.propTypes = {
        children: PropTypes.func.isRequired
    };
    

    注意事项

    将 Render Props 与 React.PureComponent 一起使用时要小心

    如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 React.PureComponent 带来的优势。因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render 对于 render prop 将会生成一个新的值。

    例如,继续我们之前使用的 < Mouse > 组件,如果 Mouse 继承自 React.PureComponent 而不是 React.Component,我们的例子看起来就像这样:

    class Mouse extends React.PureComponent {
        // 与上面相同的代码......
    }
    
    class MouseTracker extends React.Component {
        render() {
            return (
                <div>
                    <h1>Move the mouse around!</h1>
    
                        {/*
                            这是不好的!
                            每个渲染的 `render` prop的值将会是不同的。
                        */}
                        <Mouse render={mouse => (
                            <Cat mouse={mouse} />
                        )}/>
                </div>
            );
        }
    }
    

    在这样例子中,每次 < MouseTracker > 渲染,它会生成一个新的函数作为 < Mouse render > 的 prop,因而在同时也抵消了继承自 React.PureComponent 的 < Mouse > 组件的效果!

    为了绕过这一问题,有时你可以定义一个 prop 作为实例方法,类似这样:

    class MouseTracker extends React.Component {
        // 定义为实例方法,`this.renderTheCat`始终
        // 当我们在渲染中使用它时,它指的是相同的函数
        renderTheCat(mouse) {
            return <Cat mouse={mouse} />;
        }
    
        render() {
            return (
                <div>
                    <h1>Move the mouse around!</h1>
                    <Mouse render={this.renderTheCat} />
                </div>
            );
        }
    }
    

    如果你无法静态定义 prop(例如,因为你需要控制组件 props 和/或 state 的暴露程度),则 < Mouse > 应该继承自 React.Component。


    起源地下载网 » React - Render Props

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元