09_高阶组件HOC

94 阅读3分钟

高阶组件HOC

高阶函数的定义,至少满足以下条件之一:

  • 接受一个或多个函数作为输入
  • 输出一个函数

JavaScript中比较常见的filter、map、reduce都是高阶函数

那么什么是高阶组件呢?

  • 高阶组件的英文是 Higher-Order Compoents,简称HOC
  • 官方的定义:高阶组件是参数为组件,返回值为新组件的函数
  • 高阶组件本身不是一个组件,而是一个函数
  • 这个函数的参数是一个组件,返回值也是一个组件

高阶组件并不是React API的一部分,它是基于React的组合特性而形成的设计模式

高阶组件在一些React第三方库中非常常见:

  • rudux 中的connect

高阶组件应用-props增强

import React, { PureComponent } from 'react'

// 定义组件: 给一些需要特殊数据的组件, 注入props
function enhancedUserInfo(OriginComponent, otherProps = {}) {
  class NewComponent extends PureComponent {
    constructor(props) {
      super(props)

      this.state = {
        userInfo: {
          name: "jay chou",
          age: 40
        }
      }
    }

    render() {
      return <OriginComponent {...this.props} {...this.state.userInfo} {...otherProps}/>
    }
  }
  return NewComponent
}

const Home = enhancedUserInfo(function(props) {
  return <h1>Home: {props.name}-{props.age}-{props.banners}</h1>
})

const User = enhancedUserInfo(function(props) {
  return <h1>Profile: {props.name}-{props.age}-{props.height}</h1>
}, {height: 174})

export class App extends PureComponent {
  render() {
    return (
      <div>
        <Home banners={["轮播1", "轮播2"]}/>
        <User/>
      </div>
    )
  }
}

export default App

高阶组件应用-共享Context

import React, { PureComponent } from 'react'
import { createContext } from "react"

const ThemeContext = createContext({color: '#f40'})

function withTheme(OriginComponment) {
  return (props) => {
    return (
      <ThemeContext.Consumer>
        {
          value => <OriginComponment {...value} {...props}/>
        }
      </ThemeContext.Consumer>
    )
  }
}

class Product extends PureComponent {
  render() {
    const {color, size} = this.props
    return (
      <div>
        <h1>Product: {color}-{size}</h1>
      </div>
    )
  }
}

const NewProduct = withTheme(Product)

export class App extends PureComponent {
  render() {
    return (
      <div>
        <ThemeContext.Provider value={{color: "blue", size: 30}}>
          <NewProduct />
        </ThemeContext.Provider>
      </div>
    )
  }
}

export default App

高阶组件应用-登录鉴权

在开发中,有些页面是需要用户登录后才能进入的

import React, { PureComponent } from 'react'

const loginAuth = function(OriginComponment) {
  return (props) => {
    const token = localStorage.getItem("token")
    
    if(token) {
      return <OriginComponment {...props}></OriginComponment>
    } 
    return <div>您尚未登录,不能查看该页面</div>
  }
}

const User = loginAuth(function() {
  return (
    <div>User Page</div>
  )
})

export class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      isLogin: false,
    }
  }
  loginClick() {
    localStorage.setItem("token", "jshjbhdasjk")
    this.setState({ isLogin: true }) //高阶组件没有使用memo 所以会重新渲染
    // this.forceUpdate() //强制更新
  }

  render() {
    return (
      <div>
        <button onClick={e => this.loginClick()}>登录</button>
        <User />
      </div>
    )
  }
}

export default App

高阶组件应用-生命周期劫持

import { PureComponent } from "react";

function logRenderTime(OriginComponent) {
  return class extends PureComponent {
    UNSAFE_componentWillMount() {
      this.beginTime = new Date().getTime()
    }

    componentDidMount() {
      this.endTime = new Date().getTime()
      const interval = this.endTime - this.beginTime
      console.log(`当前${OriginComponent.name}页面花费了${interval}ms渲染完成!`)
    }

    render() {
      return <OriginComponent {...this.props}/>
    }
  }
}

class Detail extends PureComponent {
  render() {
    return (
      <div>
        <h2>Detail Page</h2>
        <ul>
          <li>数据列表1</li>
          <li>数据列表2</li>
          <li>数据列表3</li>
          <li>数据列表4</li>
          <li>数据列表5</li>
          <li>数据列表6</li>
          <li>数据列表7</li>
          <li>数据列表8</li>
          <li>数据列表9</li>
          <li>数据列表10</li>
        </ul>
      </div>
    )
  }
}

const NewDetail = logRenderTime(Detail)

class App extends PureComponent {
  render() {
    return (
      <div>
        <NewDetail />
      </div>
    )
  }
}

export default App

高阶组件的意义

利用高阶组件可以针对某些React代码进行更优雅的处理

在React早期时提供另一种代码复用方式 mixin,目前不再建议使用

  • Mixin 可能会相互依赖,相互耦合,不利于代码维护
  • 不同的Mixin中的方法可能会互相冲突
  • Mixin非常多时,组件处理起来会比较麻烦,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性

当然,HOC也有自己的一些缺陷

  • HOC需要在原组件上进行包裹或者嵌套,如果使用大量的HOC,将会产生非常多的嵌套,这让调试变得非常困难
  • HOC可以劫持props,在不遵守约定的情况下也可能造成冲突

Hooks的出现,是开创性的,它解决了很多React之前存在的问题

  • 比如this指向问题、HOC嵌套的复杂度问题等