react高阶组件

<!-- 1. React组件复用概述 -->
思考:如果两个组件中的部分功能相似或相同,该如何处理?
 处理方式:复用相似的功能(联想函数封装)
 复用什么?1. state 2. 操作state的方法 (组件状态逻辑 )
 两种方式:1. render props模式 2. 高阶组件(HOC)
 注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式(写法)

<!-- 2.实现思路 -->
在使用组件时,添加一个值为函数的prop,通过 函数参数 来获取(需要组件内部实现)
使用该函数的返回值作为要渲染的UI内容(需要组件内部实现)

<!-- 3.render props 模式 -->
使用步骤
1. 创建Mouse组件,在组件中提供复用的状态逻辑代码(1. 状态 2. 操作状态的方法)
2. 将要复用的状态作为 props.render(state) 方法的参数,暴露到组件外部
3. 使用 props.render() 的返回值作为要渲染的内容

    import React from 'react'
    import ReactDOM from 'react-dom'
    /*  render props 模式*/
    // 导入图片资源
    import img from './images/cat.png'
    // 作用:鼠标位置复用
    class Mouse extends React.Component {    //Mouse组件负责:封装复用的状态逻辑代码(1. 状态 2. 操作状态的方法)
      // 鼠标位置state                       //状态:鼠标坐标(x, y)
      state = {      
        x: 0,   y: 0
      }

      // 鼠标移动事件的事件处理程序    操作状态的方法:鼠标移动事件
      handleMouseMove = e => {
        this.setState({
          x: e.clientX,
          y: e.clientY
        })
      }
      // 监听鼠标移动事件
      componentDidMount() {
        window.addEventListener('mousemove', this.handleMouseMove)
      }
      render() {     //复用方法        传入的render prop负责:使用复用的状态来渲染UI结构
        return this.props.render(this.state)  //子组件接收数据,并传参调用方法
      }
    }

    class App extends React.Component {
      render() {
        return (
          <div>
            <h1>render props 模式</h1>
            <Mouse
              render={mouse => {     //父组件属性绑定方法,并return ui结构
                return (
                  <p>
                    鼠标位置:{mouse.x} {mouse.y}
                  </p>
                )
              }}
            />

            {/* 猫捉老鼠 */}
            <Mouse
              render={mouse => {
                return (
                  <img
                    src={img}
                    alt="猫"
                    style={{
                      position: 'absolute',
                      top: mouse.y - 64,
                      left: mouse.x - 64
                    }}
                  />
                )
              }}
            />
          </div>
        )
      }
    }
    ReactDOM.render(<App />, document.getElementById('root'))

<!-- 4.children代替render属性 -->
注意:并不是该模式叫 render props 就必须使用名为render的prop,实际上可以使用任意名称的prop
 把prop是一个函数并且告诉组件要渲染什么内容的技术叫做:render props模式
 推荐:使用 children 代替 render 属性

代码优化:
1. 推荐:给 render props 模式添加 props校验
2. 应该在组件卸载时解除 mousemove 事件绑定

    import React from 'react'
    import ReactDOM from 'react-dom'
    import PropTypes from 'prop-types'

    // 作用:鼠标位置复用
    class Mouse extends React.Component {
      state = {          // 鼠标位置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)
      }
      render() {               //方法
        return this.props.children(this.state)
      }
    }
    // 添加props校验
    Mouse.propTypes = {
      children: PropTypes.func.isRequired
    }

    class App extends React.Component {
      render() {
        return (
          <div>
            <h1>render props 模式</h1>
            <Mouse>                   //组件children属性
              {mouse => {
                return (
                  <p>
                    鼠标位置:{mouse.x} {mouse.y}
                  </p>
                )
              }}
            </Mouse>
          </div>
        )
      }
    }
    ReactDOM.render(<App />, document.getElementById('root'))

<!-- 5.高阶组件概述 -->
目的:实现状态逻辑复用
 采用 包装(装饰)模式 ,比如说:手机壳
 手机:获取保护功能
 手机壳 :提供保护功能
 高阶组件就相当于手机壳,通过包装组件,增强组件功能

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

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

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

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

    import React from 'react'
    import ReactDOM from 'react-dom'
    /* 高阶组件*/
    // 创建高阶组件
    function withMouse(WrappedComponent) {
      // 该组件提供复用的状态逻辑
      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)
        }

        render() {
          console.log('Mouse:', this.props)
          return <WrappedComponent {...this.state} {...this.props} />       //传递props
        }
      }

      // 设置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 MousePosition = withMouse(Position)

    class App extends React.Component {
      render() {
        return (
          <div>
            <h1>高阶组件</h1>
            <MousePosition a="1" />
          </div>
        )
      }
    }
    ReactDOM.render(<App />, document.getElementById('root'))
原文地址:https://www.cnblogs.com/xm0328/p/14013660.html