React-render-props和高阶组件

一、组件复用概述

  在实际开发中,如果有个功能基本一致,那么如何来进行处理:

  就是将两个功能,进行抽离封装成一个公用的组件,来进行使用,来进行复用(联想函数封装)

  在封装组件的过程中,需要封装哪些东西:1.state -- 数据 2.操作数据的方法

  React 中的解决方案(两种方式):render-props、高阶组件

二、render props 模式

import React from 'react'

/**
 * 1、先创建组件,在组件内部声明 复用的 state 以及操作 state 的方法
 * 2、将要复用的数据作为 props.render(state) 方法的参数,暴露到组件外部
 */

 // 不能使用函数组件,因为函数组件没有自己的私有数据
 //  1、先创建组件,在组件内部声明 复用的 state 以及操作 state 的方法
 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)
   }

   render() {
     // 2、要复用的数据作为 props.render(state) 方法的参数,暴露到组件外部
    //  return this.props.render(this.state)
    return this.props.render()
   }
 }

 class App extends React.Component {
   render() {
     return (
       <div>
         {/* 在使用组件时,添加一个值为函数的prop,通过函数传参  */}
         <Mouse render={(Mouse) => (
           // 如何渲染任意的 UI 
           // 使用该函数的返回值作为要渲染的 UI 内容
          <div>
            <p>x:{Mouse.x}</p>
            <p>y:{Mouse.y}</p>
          </div>
         )}/>
       </div>
     )
   }
 }

 export default App

使用 children 替换 render

import React from 'react'
import img from '../images/cat.png'

// 定义一个复用组件
class Mouse extends React.Component {
  // 1、定义数据
  state = {
    x: 0,
    y: 0
  }

  // 2、定义方法
  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }

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

  render() {
    return this.props.children(this.state)
  }
}

class App extends React.Component {
  render() {
    return(
      <div>
        {/* 鼠标移动 */}
        <Mouse>
        {
          mouse => (
            <div>
              <p>x: {mouse.x}</p>
              <p>y: {mouse.y}</p>
            </div>
          )
        }          
        </Mouse>
        {/* 猫捉老鼠 */}
        <Mouse>
          {
            mouse => (
              <img src={img} style={{position: 'absolute',top: mouse.y,left: mouse.x}}/>
            )
          }
        </Mouse>
      </div>
    )
  }
}

export default App

三、高阶组件

import React from 'react'
import img from '../images/cat.png'

/**
 * 高阶组件说白了就是包裹、嵌套
 */

// 1.创建一个函数,名称以 with 开头
// 2.指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
// WrapperComponent 这是一个形参,代表是以后要增强的组件
function withMouse(WrapperComponent) {
  // 3、创建类组件,并返回
  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)
    }

    // 4、在类组件内部,返回传入的组件,并传递复用的数据
    render() {
      return <WrapperComponent {...this.state} />
    }
  }

  Mouse.displayName = `with${getDisplayName(WrapperComponent)}`

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

  return Mouse
}

// 5、写 UI 结构,用来测试高阶组件
const Position = props => {
  return (
    <p>
      鼠标当前位置:(x: {props.x}, y: {props.y})
    </p>
  )
}

// 猫捉老鼠
const Cat = props => {
  return (
    <img src={img} alt="" style={{
      position: 'absolute',
      top: props.y - 64,
      left: props.x - 64
    }}/>
  )
}

// 6、调用高阶组件,传入要增强的组件,会返回一个增强的组件
const MousePosition = withMouse(Position)
const MouseCat = withMouse(Cat)

class App extends React.Component {
  render() {
    return (
      <div>
        <MousePosition></MousePosition>
        <MouseCat></MouseCat>
      </div>
    )
  }
}

export default App

四、高阶组件添加 displayName

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

  ⑤ 添加方式:

Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component' }
import React from 'react'
import img from '../images/cat.png'

/**
 * 高阶组件说白了就是包裹、嵌套
 */

// 1.创建一个函数,名称以 with 开头
// 2.指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
// WrapperComponent 这是一个形参,代表是以后要增强的组件
function withMouse(WrapperComponent) {
  // 3、创建类组件,并返回
  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)
    }

    // 4、在类组件内部,返回传入的组件,并传递复用的数据
    render() {
      return <WrapperComponent {...this.state} />
    }
  }

  Mouse.displayName = `with${getDisplayName(WrapperComponent)}`

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

  return Mouse
}

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

// 猫捉老鼠
const Cat = props => {
  return (
    <img src={img} alt="" style={{
      position: 'absolute',
      top: props.y - 64,
      left: props.x - 64
    }}/>
  )
}

// 5、调用高阶组件,传入要增强的组件,会返回一个增强的组件
const MousePosition = withMouse(Position)
const MouseCat = withMouse(Cat)

class App extends React.Component {
  render() {
    return (
      <div>
        <MousePosition></MousePosition>
        <MouseCat></MouseCat>
      </div>
    )
  }
}

export default App

猜你喜欢

转载自www.cnblogs.com/xiaowzi/p/12368706.html