React高阶组件详解

高阶组件是什么?

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

高阶组件是参数为组件,返回值为新组件的函数

在此期间,我们可以对该组件进行props处理,事件处理等工作

认识高阶组件

  1. 复用逻辑

    对组件进行加工处理,根据需求来定制专属化的HOC

  2. 强化props

    劫持上一层传过来的props,混入新的props

  3. 赋能组件

    HOC有一项独特的特性,就是可以给被HOC包裹的业务组件,提供一些拓展功能,比如说额外的生命周期,额外的事件,但是这种HOC,可能需要和业务组件紧密结合

  4. 控制渲染

    劫持渲染是hoc一个特性,在wrapComponent包装组件中,可以对原来的组件,进行条件渲染,节流渲染,懒加载等功能

使用方式

  • 装饰器模式

  • 函数包裹

两种不同的高阶组件

  • 正向的属性代理

      function HOC(WrapComponent){
        return class Advance extends React.Component{
          state={
              name:'loong'
          }
          render(){
              return <WrapComponent  { ...this.props } { ...this.state }  />
          }
        }
      }
    

    我们来看一下上面这个例子,return 返回结果是父组件(代理组件)对子组件(业务组件)的一系列操作

    在 fiber tree上,先mounted组件,然后才是我们的业务组件

  • 反向的组件继承

        class Index extends React.Component{
            render(){
                return <div> hello,world  </div>
            }
        }
        function HOC(Component){
            return class wrapComponent extends Component{
    
            }
        }
        export default HOC(Index) 
    

    HOC采用继承的方式, 代理组件继承了业务组件的本身,我们在使用的时候直接实例化代理组件HOC即可

编写高阶组件

很多文章对其有很多分类,我这里就按照我的理解去分类
  1. 增强props
    混入props 代理组件state状态会混入(上面有混入的例子), 初次之外我们还可以进行控制state的更新

    function classHOC(WrapComponent){
        return class  Idex extends React.Component{
            constructor(){
                super()
                this.state={
                    name:'alien'
                }
            }
            changeName(name){
                this.setState({ name })
            }
            render(){
                return <WrapComponent { ...this.props }  { ...this.state } changeName={this.changeName.bind(this)  }  />
            }
        }
    }
    function Index(props){
        const [ value ,setValue ] = useState(null)
        const { name ,changeName } = props
        return <div>
            <div>   hello,world , my name is { name }</div>
            改变name <input onChange={ (e)=> setValue(e.target.value)  }  />
            <button onClick={ ()=>  changeName(value) }  >确定</button>
        </div>
    }
    
    export default classHOC(Index)
    

    我们看这个例子,代理组件中changeName={this.changeName.bind(this)} ,绑定changeName方法。用来更新name属性(代理组件中,说实话让我想起来了闭包)。

    题外话:上面的例子中changeName绑定的是一个函数,然后利用bind改变其this指向(返回值是一个函数),在Index组件使用的时候<button onClick={ ()=> changeName(value) } >确定</button> , 才可以正确更新到代理组建的state。如果不绑定就会找不到该方法。

  2. 控制渲染
    代理组件通过处理可以控制子组件是否显示

    可以通过变量控制,也可以通过其它方式。达到控制渲染的目的就可以了

    我们来看一个优化的例子💨

        function HOC (Component){
            return function renderWrapComponent(props){
                const { num } = props
                const RenderElement = useMemo(() =>  <Component {...props}  /> ,[ num ])
                return RenderElement
            }
        }
        class Index extends React.Component{
        render(){
            console.log(`当前组件是否渲染`,this.props)
            return <div>hello,world, my name is alien </div>
        }
        }
        const IndexHoc = HOC(Index)
    
        export default ()=> {
            const [ num ,setNumber ] = useState(0)
            const [ num1 ,setNumber1 ] = useState(0)
            const [ num2 ,setNumber2 ] = useState(0)
            return <div>
                <IndexHoc  num={ num } num1={num1} num2={ num2 }  />
                <button onClick={() => setNumber(num + 1) } >num++</button>
                <button onClick={() => setNumber1(num1 + 1) } >num1++</button>
                <button onClick={() => setNumber2(num2 + 1) } >num2++</button>
            </div>
        }
    

    上面的例子应该很好理解,代理组件通过useMemo钩子来依据props传入的num,来决定是否更新子组件

  3. 劫持生命周期

    function HOC (Component){
      const proDidMount = Component.prototype.componentDidMount 
      Component.prototype.componentDidMount = function(){
         console.log('劫持生命周期:componentDidMount')
         proDidMount.call(this)
      }
      return class wrapComponent extends React.Component{
          render(){
            return <Component {...this.props}  />
          }
      }
    }
    

    也是很好理解,代理组件保存子组建的生命周期函数,然后重新设置对应的生命周期函数

  4. 事件处理

    我们来看分析下面这个例子💨

    function ClickHoc (Component){
      return  function Wrap(props){
        const dom = useRef(null)
        useEffect(()=>{
         const handerClick = () => console.log('发生点击事件') 
         dom.current.addEventListener('click',handerClick)
         return () => dom.current.removeEventListener('click',handerClick)
        },[])
        return  <div ref={dom} ><Component  {...props} /></div>
      }
    }
    

    其实就是代理组件上面加了一个click监听事件

  5. 获取自定义组件ref

    我们知道在自定义组件上面ref是无用的,我们可以使用代理组件包裹业务组件即可,ref={dom}在代理组件上,即可获取

     return  <div ref={dom} ><Component  {...props} /></div>
    

高阶组件实践

可以参卡`推荐文章`中的这一章节,写的很详细

推荐文章

「react进阶」一文吃透React高阶组件(HOC)

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李和贵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值