react 纯函数组件 使用高阶组件

117 阅读1分钟

写在前面:让普通组件,通过高阶函数的包裹,变为高阶组件,拿到鼠标的坐标值

import React, { useState, useEffect, CSSProperties } from 'react';

interface Iprops extends React.ReactElement {
  mouseX: number,
  mouseY: number,
  [key: string]: any
}

interface StyleFunc {
  (x: number, y: number): CSSProperties
}

/**
 * @desciption 被复用的高阶组件都是同名,修改组件名字后便于调试
 * @param WrappedComponent 组件
 * @param defualtName 默认名
 * @return WrappedComponent.displayName 组件的修改名 ||
 *         WrappedComponent.name 组件名 ||
 *         defaultName 默认名
*/
function getDisplayName(WrappedComponent: React.FC<Iprops>, defualtName?: string) {
  return WrappedComponent.displayName || WrappedComponent.name || defualtName;
}

/**
 * @desciption 高阶组件函数
 * @param WrappedComponent 被包裹的组件
 * @return 可以复用的逻辑组件
*/
const withMouse = (WrappedComponent: React.FC<Iprops>) => {
  const Mouse = (props: any) => {
    const [mouseXY, setMouseXY] = useState<Pick<Iprops, 'mouseX' | 'mouseY'>>({
      mouseX: 0,
      mouseY: 0
    });

    // 调试高阶组件传过来的props, 并传给组件WrappedComponent
    useEffect(() => {
      console.log(props);  // { a: 1 } {}
    }, []);

    const handleMouseMove = (e: MouseEvent) => {
      console.log(e.clientX, e.clientY);
      setMouseXY({
        mouseX: e.clientX,
        mouseY: e.clientY
      })
    }

    // 添加鼠标移动事件
    useEffect(() => {
      window.addEventListener('mousemove', handleMouseMove)
      return () => {
        window.removeEventListener('mousemove', handleMouseMove);
      }
    }, [])

    return <WrappedComponent {...mouseXY} {...props} />
  }

  // 修改组件名
  Mouse.displayName = `withMouse${getDisplayName(WrappedComponent, 'Mouse')}`
  return Mouse;
}

// 普通组件PositionShow
const PositionShow: React.FC<Iprops> = (props) => {
  return (
    <p>横坐标为:{props.mouseX},纵坐标为{props.mouseY}</p>
  )
}

// 普通组件RecMove:跟随鼠标移动的盒子
const RecMove: React.FC<Iprops> = (props) => {

  const style: StyleFunc = (x, y) => ({
    width: 100,
    height: 100,
    backgroundColor: '#f00',
    position: 'fixed',
    zIndex: 100,
    left: x,
    top: y,
    marginLeft: -50,
    marginTop: -50,
    // 点击穿透:自动冒泡到父级元素
    pointerEvents: 'none'
  })

  return (
    <div style={style(props.mouseX, props.mouseY)}></div>
  )
}

// 高阶组件
const HighPositionShow = withMouse(PositionShow);
const HighRecMove = withMouse(RecMove);

// 路由页面组件
const HighComponentPages = () => {
  return <>
    {/* 在调试中默认显示的组件名都为
            <Mouse/>
            <Mouse/>
        在修改displayName之后,显示的组件名为
            <withPositionShow/>
            <withMouseRecMove/>
    */}
    <HighPositionShow a="1" />
    <HighRecMove />
  </>

}

export default HighComponentPages;