深入react 高阶组件

高阶组件:
返回值是一个函数的那么一个函数 基本算是个类工厂方法
W (WrappedComponent) 是被包裹的 React.Component;而函数返回的 E (Enhanced Component) 则是新得到的 HOC,也是个 React.Component
使用场景:
  • 属性代理:由 HOC 操纵那些被传递给被包裹组件 W 的 props
  • 继承反转:HOC 继承被包裹组件 W

hoc能做什么
在大的维度上 HOC 能用于:
  • 代码重用和逻辑抽象
  • render 劫持
  • state 抽象和操纵
  • 操纵属性(props)
属性代理
const propsProxy = (props)=>( WrappedComponent)=>{
return class PP extends React.Component {    render() {      return <WrappedComponent {...props }{..this.props}/>    }  }
}
此处关键的部分在于 HOC 的 render() 方法 返回 了一个被包裹组件的 React Element。同时,将 HOC 接受到的属性传递给了被包裹的组件,因此称为 “属性代理”
可以用属性代理做些什么?
  • 操纵属性
  • 通过 refs 访问实例
  • 抽象 state
  • 包裹组件
可以对传递给被包裹组件的属性进行增删查改。但删除或编辑重要属性时要谨慎,应合理设置 HOC 的命名空间以免影响被包裹组件。
操纵属性
例子:增加新属性。应用中通过 this.props.user 将可以得到已登录用户

const ppHOC=(currentLoggedInUser)=>(WrappedComponent) =>{  return class PP extends React.Component {    render() {      const newProps = {        user: currentLoggedInUser      }      return <WrappedComponent {...newProps}/>    }  }}
通过 refs 访问实例
可以通过  ref  访问到  this (被包裹组件的实例),但这需要  ref  所引用的被包裹组件运行一次完整的初始化 render 过程,这就意味着要从 HOC 的 render 方法中返回被包裹组件的元素,并让 React 完成其一致性比较过程,而  ref  能引用该组件的实例就好了。
例子:下例中展示了如何通过 refs 访问到被包裹组件的实例方法和实例本身
可用在应用echarts等第三方库时候的场景

function refsHOC(WrappedComponent) {  return class RefsHOC extends React.Component {    proc(wrappedComponentInstance) {      wrappedComponentInstance.method()    }        render() {      const props = Object.assign({}, this.props, {ref: this.proc.bind(this)})      return <WrappedComponent {...props}/>    }  }}

该高阶组件可以作为一个组件的方法使用
抽象 state
通过提供给被包裹组件的属性和回调,可以抽象 state,这非常类似于 smart 组件是如何处理 dumb 组件的。

例子:在下面这个抽象 state 的例子里我们简单的将 value 和 onChange 处理函数从 name 输入框中抽象出来。

function ppHOC(WrappedComponent) {  return class PP extends React.Component {    constructor(props) {      super(props)      this.state = {        name: ''      }            this.onNameChange = this.onNameChange.bind(this)    }    onNameChange(event) {      this.setState({        name: event.target.value      })    }    render() {      const newProps = {        name: {          value: this.state.name,          onChange: this.onNameChange        }      }      return <WrappedComponent {...this.props} {...newProps}/>    }  }}
用起来可能会是这样的:
@ppHOCclass Example extends React.Component {  render() {    return <input name="name" {...this.props.name}/>  }}
于是这个输入框就自动成为了一个受控组件。
包裹组件
可以利用组件的包裹,实现样式定义、布局或其他目标。一些基础用法可以由普通的父组件完成(参阅附录B),但如前所述,用 HOC 可以更加灵活。
例子:为定义样式而实现的包裹
function ppHOC(WrappedComponent) {  return class PP extends React.Component {    render() {      return (        <div style={{display: 'block'}}>          <WrappedComponent {...this.props}/>        </div>      )    }  }}
继承反转
function iiHOC(WrappedComponent) {  return class Enhancer extends WrappedComponent {    render() {      return super.render()    }  }}
被返回的 HOC 类(强化过的类) 继承 了被包裹的组件。之所以被称为“继承反转”是因为,被包裹组件并不去继承强化类,而是被动的让强化类继承。通过这种方式,两个类的关系看起来 反转 了。
继承反转使得 HOC 可以用   this   访问被包裹组件的实例,这意味着 可以访问 state、props、组件生命周期钩子,以及 render 方法
可以用继承反转做些什么?
  • render 劫持
  • 操纵 state
render 劫持
称之为“render 劫持”是因为 HOC 控制了被包裹组件的 render 输出,并能对其做任何事情。
在 render 劫持中可以:
  • 在任何 render 输出的 React Elements 上增删查改 props
  • 读取并修改 render 输出的 React Elements 树
  • 条件性显示元素树
  • 出于定制样式的目的包裹元素树(正如属性代理中展示的)
就如我们之前学到的,继承反转 HOC 不保证处理完整的子树,这意味着 render 劫持技术有一些限制。经验法则是,借助于 render 劫持,可以不多不少的操作被包裹组件的 render 方法输出的元素树。如果那个元素数包含了一个函数类型的 React Component,那就无法操作其子组件(被 React 的一致性比较过程延迟到真正渲染到屏幕上时)。
例子1:条件性渲染
function iiHOC(WrappedComponent) {  return class Enhancer extends WrappedComponent {    render() {      if (this.props.loggedIn) {        return super.render()      } else {        return null      }    }  }}

例子2:修改 render 输出的元素树
function iiHOC(WrappedComponent) {  return class Enhancer 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    }  }}
本例中,如果由 render 输出的被包裹组件有一个 input 顶级元素,就改变其 value。
可以在这里做任何事情,可以遍历整个元素树并改变其中的任何一个元素属性。


操纵 state
HOC 可以读取、编辑和删除被包裹组件实例的 state,也可以按需增加更多的 state。要谨记如果把 state 搞乱会很糟糕。大部分 HOC 应该限制读取或增加 state,而后者(译注:增加 state)应该使用命名空间以免和被包裹组件的 state 搞混。

例子:对访问被包裹组件的 props 和 state 的调试
export function IIHOCDEBUGGER(WrappedComponent) {  return class II extends WrappedComponent {    render() {      return (        <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>          {super.render()}        </div>      )    }  }}
该 HOC 将被包裹组件嵌入其他元素中,并显示了其 props 和 state。
使用 HOC 时,就失去了被包裹组件原有的名字,可能会影响开发和调试。
hoc总结:
  • render 劫持(在继承反转中看见过)
  • 控制内部 props(同样在继承反转中看见过)
  • 抽象 state,但存在缺点。将无法在外部访问父元素的 state,除非特意为止创建钩子。这限制了其实用性
  • 包裹新的 React Elements。这可能是父组件唯一强于 HOC 的用例,虽然 HOC 也能做到

  • 操纵子组件有一些陷阱。比如说如果 children 的根一级并不只有单一的子组件(多于一个的第一级子组件),你就得添加一个额外的元素来收纳所有子元素,这会让你的代码有些冗繁。在 HOC 中一个单一的顶级子组件是被 React/JSX 的约束所保证的。

转自:https://mp.weixin.qq.com/s/dtlrOGTjoneOIiM5kB3XvQ(部分理解稍做修改)

猜你喜欢

转载自blog.csdn.net/qq_37653449/article/details/80652628