React 高阶组件

                                               React  高阶组件

一.  高阶组件的意义

     1.  代码复用 : 高阶组件的基本功能,提取多个组件相似度高的部分,在通过高阶组件扩展。

     2.  条件渲染: 控制组件的渲染逻辑, 常见case:  鉴权

     3.  生命周期捕获/劫持: 借助父子组件生命周期规则捕获子组件的生命周期;

二. 高阶组件遵循的一些原则

     1.  不要修改原始组件

     2.  props保持一致

     3.  保持可组合性

     4.  displayName  为了方便调试,最常见的高阶组件命名方式是将子组件名字包裹起来。

     5.  不要render 方法内部使用高阶组件 (render中的高阶组件会在每次render时重新mount, 之前组件内部的state也会丢失) 

三. 使用方法对比

     高阶组件使用有几种不同的方式,可以从如下几个方面来分析他们之间的差异。

     一个React组件包含以下几个重要部分:

     · props    · state  · ref  ·生命周方法  ·static 方法  · React元素树

     注: 目前官方推荐ref属性接收一个回调函数,这个函数执行的时机为:

             1.  组件被挂载后,回调函数被立即执行,回调函数的参数为该组件的具体实例;

             2.  组件被卸载或者原有的ref属性本身发生变化时,回调也会被立即执行,此时回调函数参数为null, 以确保内存不泄露。

     所以不同方式的对比可以从以下几个方面进行(元组件即传入组件):

      1. 原组件所在位置: 如能否被包裹或包裹其它组件;

      2. 能否读取到或操作员组件的props;

      3. 能否读取,操作(编辑,删除)元组件的state;

      4. 能否通过ref访问到原组件中的dom元素;

      5. 是否影响原组件某些生命周期等方法;

      6. 是否取到原组件static方法;

       7. 能否劫持原组件生命周期方法;

       8. 能否渲染劫持;

四.  HOC  Demo举例

  如下代码定义了一个子组件及多个不同类型的HOC组件来测试,对子组件的内部属性,方法及状态等的访问情况。

/**
 * 高阶组件Demo
 */

 import React, { Component } from 'react'

 class HOCDemo extends Component{
   static sayHello() {
     console.log('hello from hocdemo static....')
   }

   constructor(props) {
     super(props)
     this.state={
       name: "HOC_DEMO",
       age: 66
     }
     console.log('HOCDemo Son constructor')
     this.focus = this.focus.bind(this)
   }

   componentWillMount(){
    console.log('HOCDemo Son componentWillMount')

    !!this.props.name && this.setState({
        age: this.props.age,
        name: this.props.name
    })
   }

   componentDidMount() {
    console.log('HOCDemo Son componentDidMount')
   }

   componentWillReceiveProps(nextProps) {
     console.log('HOCDemo Son componentWillReceiveProps')
     console.log(nextProps)
   }

   focus(){
     this.inputElement.focus()
   }

   render(){
    const outerStyle = {
      margin: "auto",
      text: "center"
    }
    const inputStyle = {
      border: 0,
      padding: '2px'
    }
    return (
      <div style={outerStyle}>
        <p>姓名:{this.state.name}</p>
        <p>
          年龄:
          <input
            style={inputStyle}
            value={this.state.age}
            onChange={()=>{}}
            ref={(input) => {
                this.inputElement = input;
                !!this.props.inputRef && this.props.inputRef(input)
            }}
          />
        </p>
        <p>
          <input
            type="button"
            value="focus input"
            onClick={this.focus}
          />
        </p>
      </div>
    )
   }
 }

 // HOC:
 // 父组件操作子组件里的input (父组件通过传回调函数给子组件,子组件通过该回调数将input给到父组件)
 const EnhanceWrapper = (WrappedComponent) => {
   let inputElement = null
   function handlerClick (){
      inputElement.focus()
   }
   function wrappedComponentStaic(){
     WrappedComponent.sayHello()
   }
   return props => (
    <div>
      <WrappedComponent
        inputRef={(el)=>{ inputElement = el}}
        {...props}
      />
      <input
        type="button"
        value="focus子组件input"
        onClick={handlerClick}
      />
      <input
        type="button"
        value="调用子组件static"
        onClick={wrappedComponentStaic}
      />
    </div>
   )
 }

 /* HOC :  返回一个类
√   原组件所在位置(能否被包裹或包裹其他组件)
√   能否取到或操作原组件的props
乄  能否取到或操作state
乄  能否通过ref访问到原组件中的dom元素
√   是否影响原组件生命周期等方法
√   是否取到原组件static方法
X  能否劫持原组件生命周期
乄 能否渲染劫持
 */
 const EnhanceWrapperClass = (WrappedComponent) => {
   return class WrapperComponent extends Component{
     static wrappedComponentStaic(){
       WrappedComponent.sayHello()
     }
     constructor(props) {
       super(props)
       console.log('wrappercomponent parent constructor')
       this.handleClick = this.handlerClick.bind(this)
     }
     componentWillMount(){
       console.log('WrapperComponent parent componentWillMount')
     }
     componentDidMount(){
       console.log('wrapperComponent parent componentDidMount')
     }
     handlerClick(){
       this.inputElement.focus()
     }

     render(){
       return (
        <div>
          <WrappedComponent
            inputRef={(el)=>{ this.inputElement = el}}
            {...this.props}
          />
          <input
            type="button"
            value="focus子组件input"
            onClick={this.handlerClick.bind(this)}
          />
          <input
            type="button"
            value="调用子组件static"
            onClick={this.constructor.wrappedComponentStaic}
          />
        </div>
       )
     }
   }
}


/* HOC: 
 继承原组件后,返回一个新class component
// 此种方式最大特点是允许 HOC 通过 this 访问到 WrappedComponent,所以可以读取和操作state/ref/生命周期方法
√   原组件所在位置(能否被包裹或包裹其他组件)
√   能否取到或操作原组件的props
√   能否取到或操作state
√   能否通过ref访问到原组件中的dom元素
√   是否影响原组件生命周期等方法
√   是否取到原组件static方法
√   能否劫持原组件生命周期
√   能否渲染劫持
*/
const EnhanceWrapperExtendSon = (WrappedComponent) => {
  return class WrapperComponent extends WrappedComponent {
    constructor(props){
      super(props)
      console.log('WrapperComponent parent constructor')
      console.log("HOC parent this", this)
    }

    componentDidMount() {
      // 通过super可以调用继承类的内部方法 (这里不能用this 的原因是, 内部自己调用自己没有推出条件,造成死循环)
      super.componentDidMount()
      // 父类自己的DidMount 输出
      console.log('WrapperComponent parent componentDidMount')
      
    }
    handlerClick(){
      // 这里通过this 可以拿到所继承类的内部属性(inputElement)及方法
      this.inputElement.focus()
    }
    render(){
      return (
        <div>
          {super.render()}
          <p>姓名: {this.state.name}</p>
          <input
            type="button"
            value="focus子组件input"
            onClick={this.handlerClick.bind(this)}
          />
          <input
            type="button"
            value="调用子组件static"
            onClick={WrapperComponent.sayHello}
          />
        </div>
      )
    }
  }
}

 const WrapperComponent = EnhanceWrapperExtendSon(HOCDemo)

 export default WrapperComponent

五.  现实应用场景实战

 

 

 

 

 

未完待续.....

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值