likes
comments
collection
share

从鼠标的 onmousemove 事件引申至高阶组件

作者站长头像
站长
· 阅读数 15

一、项目初始化阶段

  • npx create-react-app react-mouse
  • cd react-mouse
  • yarn start 或 npm start 或 npm run start
  • 新建 src/components/ComMouse.js 从鼠标的 onmousemove 事件引申至高阶组件

二、项目初始化数据

1.App.js出口

import React from 'react'
// 导入 ComMouse 组件 
import ComMouse from './components/ComMouse'

class App extends React.Component {
  render() {
    return <div>
      <ComMouse></ComMouse>
    </div>
  }
}
export default App;

2.index.js入口

import React from 'react'
import ReactDOM from 'react-dom'

import App from './App'

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

3.ComMouse.js定义鼠标移动距离初始值

import React from 'react'

class ComMouse extends React.Component {
    state = {
        x: 0,
        y: 0
    }
    render() {
        return <div>
            我是 ComMouse 组件
            <p>x 轴:{this.state.x} y 轴:{this.state.y}</p>
        </div>
    }
}
export default ComMouse

三、监听鼠标移动事件,获取鼠标移动距离

ComMouse.js

import React from 'react'
// ◆监听鼠标移动事件,获取坐标
class ComMouse extends React.Component {
    state = {
        x: 0,
        y: 0
    }
    // 1.注册鼠标移动的事件函数
    mouseHandler = (e) => {
        console.log(e)
        // 修改坐标值
        this.setState({
            x: e.clientX,
            y: e.clientY
        })

    }
    // 2.挂载阶段
    componentDidMount() {
        // 鼠标移动,调用 mouseHandler 函数
        document.addEventListener('mousemove', this.mouseHandler)
    }
    // 3.卸载阶段
    componentWillUnmount() {
        // 移除鼠标移动事件(解决重新进入页面会报错,刷新又好了的问题)
        document.removeEventListener('mousemove')
    }
    render() {
        return <div>
            我是 ComMouse 组件
            <p>x 轴:{this.state.x} y 轴:{this.state.y}</p>
        </div>
    }
}
export default ComMouse

从鼠标的 onmousemove 事件引申至高阶组件

四、思考进阶

思考1:

在上面的 ComMouse 组件中的数据已经写死了,如果我需要复用该组件,有的地方只要 x 轴的值,有的地方只要 y 轴的值,有的地方两个值都要,那怎么办呢?下面咱们来改装组件~安排

1.ComMouse.js

// ◆省略其他。。。
// ◆法1:下面进行复用操作(实现子向父传值,必须先实现父向子传值)
    render() {
        return  this.props.render({x:this.state.x,y:this.state.y})
    }

2.App.js

// 省略其他。。。
class App extends React.Component {
  render() {
    return <div>
      {/* ◆复用1:只需要 x 轴 */}
      <ComMouse render={(params) => {// 没解构
        return (
          <p>我是 ComMouse 组件1 x轴:{params.x}</p>
        )
      }}></ComMouse>
      
      {/* ◆复用2:需要 x 轴和 y轴 */}
      <ComMouse render={({ x, y }) => {// 直接解构了
        return (
          <p>我是 ComMouse 组件2 x轴:{x} y轴:{y}</p>
        )
      }}></ComMouse>
      
      {/* ◆复用3:只需要 y 轴坐标 */}
      <ComMouse render={({ y }) => {// 直接解构了
        return (
          <p>我是 ComMouse 组件3 y轴:{y}</p>
        )
      }}></ComMouse>
    </div>
  }
}

3.页面效果

从鼠标的 onmousemove 事件引申至高阶组件

思考2:

我还希望实现一个类似 vue 中 插槽的效果:子组件有值,就使用自己的(ComMouse.js),没有就使用父组件的(App.js),这就用到 this.props.children

1.ComMouse.js

// 先测试一下
// ◆法2:this.props.children 获取子组件的元素
       render() {
        // return  <p>我是一个 p </p> 
        return  this.props.children
    }

2.App.js

class App extends React.Component {
  render() {
    return <div>
      {/* ◆法2(先不传值) */}
      <ComMouse>
       <h1 style={{color:'red'}}>我是一个 h1</h1>
     </ComMouse>
    </div>
  }
}

3.页面效果

从鼠标的 onmousemove 事件引申至高阶组件

思考3:

从思考2, this.props.children 的使用可见,我们尝试带上括号 this.props.children(),父组件使用箭头函数 return,为思考4实现父传子、子传父作铺垫

1.ComMouse.js

// ◆法3(先测试:不传值)
    render() {
        return  this.props.children()
    }

2.App.js

import React from 'react'
// 导入 ComMouse 组件 
import ComMouse from './components/ComMouse'

class App extends React.Component {
  render() {
    return <div>
      {/* ◆法3(先不传值) */}
      <ComMouse>
      {()=>{
        return  <h1 style={{color:'red'}}>我是一个 h1</h1>
      }}
     </ComMouse>
    </div>
  }
}
export default App

3.页面效果(依然可以实现思考2的效果)

从鼠标的 onmousemove 事件引申至高阶组件

思考4:

既然实现了调用带括号,那我们可以传参啦!试一试!

1.ComMouse.js

// ◆法4 (传值)
    render() {
        return this.props.children({ x: this.state.x, y: this.state.y })
    }

2.App.js

import React from 'react'
// 导入 ComMouse 组件 
import ComMouse from './components/ComMouse'

class App extends React.Component {
  render() {
    return <div>
      {/* ◆法4(传值) */}
      <ComMouse>
        {({ x, y }) => {
          return (
            <div>
              <h1 style={{ color: 'red' }}>我是一个 h1 x轴:{x} ,y轴:{y}</h1>
            </div>
          )
        }}
      </ComMouse>
    </div>
  }
}
export default App

3.页面效果

从鼠标的 onmousemove 事件引申至高阶组件

五、引入高阶组件

再度思考:

既然获取了坐标,那我们可以应用自如啦!如果我还想实现一个图片追随鼠标移动的效果,以及实现更高级的复用,那么都写在 App.js 太混乱啦,于是引入了高阶组件,用于解决逻辑复用。

1.新建相关文件

  • 准备一张自己喜欢的图片,放入 src/assets/images/img.png
  • 新建src/index.css,书写图片样式
  • 新建 src/components/Hoc.js 作为高阶组件

从鼠标的 onmousemove 事件引申至高阶组件

2.App.js 导入 Hoc 组件

import React from 'react'
// 导入 ComMouse 组件 
// import ComMouse from './components/ComMouse'
// 导入高阶组件
import Hoc from './components/Hoc'

class App extends React.Component {
  render() {
    return <div>
      <Hoc></Hoc>
    </div>
  }
}
export default App

3. index.css设置图片样式

img {
    position: absolute;
    width: 40px;
}

4.Hoc.js实现一个高阶组件

import React from 'react'
import myImg from '../assets/images/img.jpg'
import '../index.css'

// 8.定义一个函数 解决调试工具里面组件名字 都是 Mouse 的问题
function getDisplayName(WrappedCom) {
    return WrappedCom.displayName || WrappedCom.name || 'Component'
}
// ◆高阶组件的使用(解决逻辑复用)
// 1.创建 with开头的函数组件
// 2.制定函数参数,以大写开头 WrappedCom
// 3.包一个类组件
function withMouse(WrappedCom) {
    class ComMouse extends React.Component {
        state = {
            x: 0,
            y: 0
        }
        // 注册鼠标移动的事件函数
        mouseHandler = (e) => {
            console.log(e)
            // 修改坐标值
            this.setState({
                x: e.clientX,
                y: e.clientY
            })

        }
        // 挂载阶段
        componentDidMount() {
            // 鼠标移动,调用 mouseHandler 函数
            document.addEventListener('mousemove', this.mouseHandler)
        }
        // 卸载阶段
        componentWillUnmount() {
            // 解决重新进入页面会报错,刷新又好了的问题(页面消失)
            document.removeEventListener('mousemove')
        }
        render() {
            return (
                // 5.渲染参数组件
                // <WrappedCom x={this.state.x} y={this.state.y}></WrappedCom>
                <WrappedCom {...this.state}></WrappedCom>
            )
        }
    }
    // 9.拼接组件的名字
    ComMouse.displayName = `withMouse${getDisplayName(WrappedCom)}`
    // 4.返回函数内部创建的类组件并返回
    return ComMouse
}

// 6.1定义需要增强的组件1
const Position = ({ x, y }) => {
    return <div>Position==x轴:{x} y轴:{y}</div>
}

// 6.2定义需要增强的组件2
const PositionImg = ({ x, y }) => {
    // return <div><img style={{left:x,top:y}} src={myImg} alt="" /></div>
    // 鼠标在图片中间
    return <div><img style={{ left: x - 20, top: y - 20 }} src={myImg} alt="" /></div>
}

// 7.调用函数,实现组件增强
// 类组件增强
const StrongCom = withMouse(Position)
const StrongComImg = withMouse(PositionImg)

class Hoc extends React.Component {
    render() {
        return <div>
            编程不仅仅是技术,还是艺术!Hoc
            <StrongCom></StrongCom>
            <StrongComImg></StrongComImg>
        </div>
    }
}
export default Hoc

5.页面效果(动图可能更好)

从鼠标的 onmousemove 事件引申至高阶组件