从鼠标的 onmousemove 事件引申至高阶组件
一、项目初始化阶段
- npx create-react-app react-mouse
- cd react-mouse
- yarn start 或 npm start 或 npm run start
- 新建 src/components/ComMouse.js
二、项目初始化数据
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
四、思考进阶
思考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.页面效果
思考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.页面效果
思考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的效果)
思考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.页面效果
五、引入高阶组件
再度思考:
既然获取了坐标,那我们可以应用自如啦!如果我还想实现一个图片追随鼠标移动的效果,以及实现更高级的复用,那么都写在 App.js 太混乱啦,于是引入了高阶组件,用于解决逻辑复用。
1.新建相关文件
- 准备一张自己喜欢的图片,放入
src/assets/images/img.png
- 新建
src/index.css
,书写图片样式 - 新建
src/components/Hoc.js
作为高阶组件
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.页面效果(动图可能更好)
转载自:https://juejin.cn/post/7076374699994775560