高阶组件

高阶组件类似于高阶函数,它接受React组件作为输入,输出一个新的React组件。当React组件被包裹时,高阶组件会返回一个增强的React组件,高阶组件可以提升代码的复用性、逻辑性、抽象性。

实现高阶组件有两种方式。

  • 属性代理
  • 反向继承 本篇文章主要分析属性代理的几种实现方式。

1. 属性代理

通过高阶组件来传递props,这种方法就是属性代理。下面对属性代理的几个功能进行讲解。

  • 控制props

我们可以读取、增加、编辑或是移除从WrappedComponent传进来的props但需要小心删除与编辑重要的props,应该尽可能对高阶组件的props做新的命名以防止混淆。

例如增加一个新的prop:

import React, { Component } from 'react';
const Mycontainer = (WrappedComponent) =>  
  class extends Component {
    render () {
      const newProps = {
        text: 'newText'
      }
      return <WrappedComponent {...this.props} {...newProps}/>
    }
  }
export default Mycontainer
复制代码

当调用高阶组件事,可以用text这个新的props了。对于原组件来说,只要套用这个高阶组件,新组件就会多一个属性。

import React, { Component } from 'react'
import Mycontainer from 'container/Mycontainer'
class Page1 extends Component {
  render() {
    console.log("this.props)
    return <div>测试新添加一个属性</div>
  }
}
export default Mycontainer(Page1)

复制代码
  • 通过refs使用引用

在高阶组件中,我们可以接受refs使用WrappedComponent的引用。例如:

import React, { Component } from 'react';

const Mycontainer = (WrappedComponent) =>  
  class extends Component {
    proc(wrappedComponentInstance) {
      console.log("ref回调函数被执行", wrappedComponentInstance)
      console.log(wrappedComponentInstance.props);
      wrappedComponentInstance.newText = "通过refs添加属性"
    }
    render () {
      const props = Object.assign({}, this.props, {
        ref: this.proc.bind(this),
      });
      return <WrappedComponent {...props} />
    }
  }
export default Mycontainer
复制代码
  • 抽象state
import React, { Component } from 'react';
const Mycontainer = (WrappedComponent) =>  
  class extends Component {
    constructor(props) {
      super(props);
      this.state = {
        name: '',
      }
    }

    onNameChange = (event) => {
      this.setState({
        name: event.target.value,
      })
    }

    render () {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onNameChange,
        }
      }
      return <WrappedComponent {...this.props} {...newProps}/>
    }
  }
export default Mycontainer
复制代码
  • 使用其他元素包裹WrappedComponent,这既可以为了增加样式,也可以是为了布局。
import React, { Component } from 'react';

const Mycontainer = (WrappedComponent) =>  
  class extends Component {
    render () {
      const newProps = {
        text: 'newText'
      }
      return (
        <div style={{display:"block"}}>
          <WrappedComponent {...this.prop} />
        </div>
      )
   }
  }
export default Mycontainer
复制代码

2. 反向继承

另一种高阶组件的实现方式成为反向继承,一个简单的实现

const MyContainer = (WrappedComponent) => {
    class extends WrappedComponent {
        render() {
            return super.render();
        }
    }
}
复制代码

正如所见,高阶组件返回的组件继承与WrappedComponent。因为被动的继承了WrappedComponent,所有的调用都会反向。 这种方法与属性代理不太一样。他通过继承WrappedComponent来实现,方法可以通过super来顺序调用,反向继承不能保证完整的子树被解析。 反向继承有两个比较大的特点。

  • 渲染劫持

渲染劫持指的是高阶组件可以控制WrappedComponent的渲染过程,并渲染各种各样的结果。我们可以在这个过程中在任何React元素输出的结果中读取、增加、修改、删除props,或读取或修改React树,或条件显示树,又或是用样式控制包裹树。

  1. 一个条件渲染的示例:
const MyContainer = (WrappedComponent) => {
    class extends WrappedComponent {
        render() {
            if(this.props.loggedIn) {
                return super.render();
            } else {
                return null;
            }
        }
    }
}
复制代码
  1. 我们可以对render的输出结果进行修改
const MyContainer = (WrappedComponent) => {
    class extends WrappedComponent {
        render() {
            const elementsTree = super.render();
            let newProps = {};
            if(elementsTree && elementsTree.type === 'input') {
                newProps = {
                    value:'may the force be with you'
                };
            }
            const props = Object.assign({},elementsTree.props,newProps);
            const newElementsTree = React.cloneElement(elementsTree,props,elementsTree.props.children);
            return newElementsTree;
        }
    }
}
复制代码
  • 控制state

高阶组件可以读取、修改或删除WrappedComponent实例中的state,如果需要的话,也可以增加state.但这样做,可能会让WrappedComponent组件内部变得一团糟。大部分高阶组件都应该限制读取或增加state,尤其是后者,可以通过重新命名state,以防止混淆。

const MyContainer = (WrappedComponent) => {
    class extends WrappedComponent {
        render() {
            <div>
                <h2>HOC Debugger Component</h2>
                <p>props</p><pre>{JSON.stringify(this.props,null,2)}</pre>
                <p>state</p><pre>{JSON.stringify(this.state,null,2)}</pre>
            </div>
        }
    }
}
复制代码

这个例子中,显示了WrappedComponent的props和state,以方便我们在程序中区调试他们。

3.组件命名

4.组件参数

有时,我们调用高阶组件时需要传入一些参数。

function HOCFactoryFactory(...params) {
    return function HOCFactory(WrappedComponent) {
        return class HOC extends Component {
            render() {
                return <WrappedComponent {...this.props}
            }
        }
    }
}
复制代码

转载于:https://juejin.im/post/5b9c7e62e51d450e5a73eab8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值