React 高阶组件

93 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情

高阶组件

概述

  1. 高阶组件(HOC,Higher-Order Component)是一个函数,其作用是接收要包装的组件(WrappedComponent),并返回增强后的组件(EnhancedComponent)。
const EnhancedComponent = withHOC(WrappedComponent)
  1. 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过 prop 将复用的状态传递给被包装的组件 WrappedComponent。
class Mouse extends React.Component {
    render() {
        return <WrappedComponent {...this.state} />
    }
}

分析

  • 目的:实现状态逻辑复用
  • 采用包装(装饰)模式(手机壳)
  • 手机:获取保护功能
  • 手机壳:提供保护功能
  • 高阶组件就相当于手机壳,通过包装组件,增强组件功能

使用步骤

  1. 创建一个函数,名称约定以 with 开头
  2. 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
  3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
  4. 在该组件中,渲染参数组件,同时将状态通过 prop 传递给参数组件
  5. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
// 导入ract
import React from 'react'
import ReactDOM from 'react-dom'

// 导入猫咪图片
import img from './images/cat.jpeg'

// 高阶组件withMouse
function withMouse(WrappedComponent) {
  // 公共Mouse组件,提供状态逻辑
  class Mouse extends React.Component {
    // 鼠标位置状态
    state = {
      x: 0,
      y: 0
    }
    
    // 鼠标移动事件的事件处理程序
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    
    // 监听鼠标移动事件
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    
    // 优化:在组件卸载时移除事件绑定
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    
    // 将要复用的状态作为 props.render(state) 方法的参数,暴露到组件外部
    render() {
      return (
        <WrappedComponent {...this.state}></WrappedComponent>
      )
    }
  }
  
  // 返回组件
  return Mouse
}


// 用来测试高阶组件
const Position = props => (
  <p>鼠标的位置:x:{props.x},y:{props.y}</p>
)

// 猫捉老鼠组件
const Cat = props => (
  <img src={img} alt='猫' style={
    {
      position: 'absolute',
      top: props.y - 123.25,
      left: props.x - 125,
      width: 250,
      height: 246.5
    }
  } />
)

// 获取增强后组件
const MousePosition = withMouse(Position)
const CatPosition = withMouse(Cat)

// App组件
class App extends React.Component {
  render() {
    return (
      <div>
        <h1>高阶组件</h1>
        {/* 渲染增强后的组件 */}
        <MousePosition />
        <CatPosition />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

设置 displayName

  • 使用高阶组件存在的问题:得到的两个组件名称相同
  • 原因:默认情况下,React 使用组件名称作为 displayName
  • 解决方式:为高阶组件设置 displayName 便于调试时区分不同的组件
  • displayName 的作用:用于设置调试信息(React Developer Tools 信息)
  • 设置方式:
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`

function getDisplayName(WrappedComponent) {
    return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

传递 props

  • 问题:props 丢失
  • 原因:高阶组件没有往下传递 props
  • 解决方式:渲染 WrappedComponent 时,将 state 和 this.props 一起传递给组件
  • 传递方式:
<WrappedComponent {...this.state} {...this.props} />

总结

// 导入ract
import React from 'react'
import ReactDOM from 'react-dom'

// 导入猫咪图片
import img from './images/cat.jpeg'

// 高阶组件withMouse
function withMouse(WrappedComponent) {
  // 公共Mouse组件,提供状态逻辑
  class Mouse extends React.Component {
    // 鼠标位置状态
    state = {
      x: 0,
      y: 0
    }

    // 鼠标移动事件的事件处理程序
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }

    // 监听鼠标移动事件
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }

    // 优化:在组件卸载时移除事件绑定
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }

    // 将要复用的状态作为 props.render(state) 方法的参数,暴露到组件外部
    render() {
      console.log('Mouse:', this.props)
      return (
        <WrappedComponent {...this.state} {...this.props}></WrappedComponent>
      )
    }
  }

  // 设置displayName
  Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`

  // 返回组件
  return Mouse
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

// 用来测试高阶组件
const Position = props => {
  console.log('Position:', props)
  return (< p > 鼠标的位置:x:{props.x},y:{props.y}</p >)
}

// 猫捉老鼠组件
const Cat = props => (
  <img src={img} alt='猫' style={
    {
      position: 'absolute',
      top: props.y - 123.25,
      left: props.x - 125,
      width: 250,
      height: 246.5
    }
  } />
)

// 获取增强后组件
const CatPosition = withMouse(Cat)
const MousePosition = withMouse(Position)

// App组件
class App extends React.Component {
  render() {
    return (
      <div>
        <h1>高阶组件</h1>
        {/* 渲染增强后的组件 */}
        <MousePosition a={1} />
        <CatPosition />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))