React:高阶组件(HOC)与 Render Props

高阶组件(HOC):最经典的组件逻辑复用方式

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。——React 官方

高阶组件概念类似高阶函数:接收函数作为输入,或者输出另一个函数的一类函数,就是高阶函数。

应的,高阶组件指的就是参数为组件,返回值为新组件的函数。没错,高阶组件本质上是一个函数,for example:

function withProps (WrappedComponent) {

    function targetComponent (props) (

        <div className="wrapper-container">

            <WrappedComponent {...props} />

        </div>

    );

    return targetComponent;

};

复制代码

如何通过高阶组件进行逻辑复用,一个简单的例子

    function checkUserAccess(){
        // ...
    }
    function CheckAccess(component){
        const isValid = checkoutUserAccess();
        return <component isValid={isValid}/>
    }
复制代码

通过高阶组件,可以提供额外的能力,对这部分逻辑进行封装

Render Props:逻辑复用的另一种思路

术语“render prop”是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。——React 官方

简而言之:render props 是 React 中复用组件逻辑的另一种思路,它在实现上和高阶组件有异曲同工之妙——两者都是把通用的逻辑提取到某一处。区别主要在于使用层面,高阶组件的使用姿势是用“函数”包裹“组件”,而 render props 恰恰相反,它强调的是用“组件”包裹“函数”。

for example:

    function RenderChildren(props){
        return (
        <>
        {props.children(props)}
        </>
        )
    }
复制代码

这里有两点:

  1. render props 的载体应该是一个React 组件,这一点是与高阶组件不同的(高阶组件本质是函数);

  2. render props 组件正常工作的前提是它的子组件需要以函数形式存在。

第一点比较好理解,主要是第二点:
我们直接看render props的使用方式

<RenderChildren>         
  {() => <p>我是 RenderChildren 的子组件</p>}       
</RenderChildren>
复制代码

这与我们经常写的react组件有区别,平时我们的写法都是

<RenderChildren>         
  <children/>     
</RenderChildren>
复制代码

但在 render props 这种模式下,它要求被 render props 组件标签包裹的一定是个函数,也就是所谓的“函数作为子组件传入”。这样一来,render props 组件就可以通过调用这个函数,传递 props,从而实现和目标组件的通信了。

render props 是如何实现逻辑复用的?看个例子,跟上面一样还是使用getAccess的例子

   function checkUserAccess(){
        // ...
    }
    function CheckAccess(props){
        const isValid = checkUserAccess();
        return (
            <>
            {
                props.children({...props,isValid})
            }
            </>
        )
    }
    
    
    //... 
    <CheckAccess>
    {
        (props) => {
        const { isAccessible } = props;
        return <ChildComponent {...props} isAccessible={isAccessible} />
            
        }
    }
    </CheckAccess>
复制代码

当然,这里并非说函数一定要作为children传入,只要可以被render props组件接收的,任意的函数名都可以

   function checkUserAccess(){
        // ...
    }
    function CheckAccess(props){
        const isValid = checkUserAccess();
        return (
            <>
            {
                props.targetComponent({...props,isValid})
            }
            </>
        )
    }
    
    
    //... 
    <CheckAccess
        targetComponent = {
            (props) => {
                const { isAccessible } = props;
                return <ChildComponent {...props} isAccessible={isAccessible} />
            }
        }
    >
    </CheckAccess>
复制代码

这里有个疑问了,render props相比于HOC有什么特别之处?

render props 将是你更好的选择,因为它更灵活

render props 和高阶组件一个非常重要的区别,在于对数据的处理上:在高阶组件中,目标组件对于数据的获取没有主动权,数据的分发逻辑全部收敛在高阶组件的内部;而在 render props 中,除了父组件可以对数据进行分发处理之外,子组件也可以选择性地对数据进行接收。

for example:
我们现在有一个NewComponent组件,也需要进行鉴权,但是由于历史原因,这个组件无法接受isValid参数,只能接受isAccess,那如果用HOC的方式,我们就需要对包装函数做处理:

    function checkUserAccess(){
        // ...
    }
    function CheckAccess(component){
        const isValid = checkoutUserAccess();
        if(component.displayName === 'newComponent'){
            return <component isAccess={isAccess}/>
        }
        return <component isValid={isValid}/>
    }
复制代码

而同样的情况。render props的方式,我们仅仅需要修改为

    // ...  
    <CheckAccess
        targetComponent = {
            (props) => {
                const { isAccessible } = props;
                return <ChildComponent {...props} isValid={isValid} />
            }
        }
    >
    </CheckAccess>
复制代码

事实上,在软件设计模式中,有一个非常重要的原则,叫“开放封闭原则”。一个好的模式,应该尽可能做到对拓展开放,对修改封闭。

处理同样的需求,render props 就能够在保全“开放封闭”原则的基础上,帮我们达到目的。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享