【react】使用useEffect操作dom

2023-09-22 15:23:53

简言

在学习react时,需要了一个需要在useEffect里操作dom的用法。
一般不推荐这么干,如果是函数组件在一渲染已挂载后立即需要操作dom绑定事件等可以参考下面解决方法。

描述

官网交错运动示例

在这里插入图片描述

这个示例中,usePointerPosition() Hook 追踪当前指针位置。尝试移动光标或你的手指到预览区域上方,可以看到有一个红点随着你移动。它的位置被保存在变量 pos1 中。

事实上,有 5(!)个正在被渲染的不同红点。你看不见是因为他们现在都显示在同一位置。这就是你需要修复的问题。你想要实现的是一个“交错”运动:每个圆点应该“跟随”它前一个点的路径。例如如果你快速移动光标,第一个点应该立刻跟着它,第二个应该在小小的延时后跟上第一个点,第三个点应该跟着第二个点等等。

你需要实现自定义 Hook useDelayedValue。它当前的实现返回的是提供给它的 value。而你想从 delay 毫秒之前返回 value。你可能需要一些 state 和一个 Effect 来完成这个任务。

实现 useDelayedValue 后,你应该看见这些点一个接一个运动。

解决答案:
app.jsx

import { useState, useEffect } from 'react';
import { usePointerPosition } from './usePointerPosition.js';

function useDelayedValue(value, delay) {
  const [delayedValue, setDelayedValue] = useState(value);

  useEffect(() => {
    setTimeout(() => {
      setDelayedValue(value);
    }, delay);
  }, [value, delay]);

  return delayedValue;
}

export default function Canvas() {
  const pos1 = usePointerPosition();
  const pos2 = useDelayedValue(pos1, 100);
  const pos3 = useDelayedValue(pos2, 200);
  const pos4 = useDelayedValue(pos3, 100);
  const pos5 = useDelayedValue(pos3, 50);
  return (
    <>
      <Dot position={pos1} opacity={1} />
      <Dot position={pos2} opacity={0.8} />
      <Dot position={pos3} opacity={0.6} />
      <Dot position={pos4} opacity={0.4} />
      <Dot position={pos5} opacity={0.2} />
    </>
  );
}

function Dot({ position, opacity }) {
  return (
    <div style={{
      position: 'absolute',
      backgroundColor: 'pink',
      borderRadius: '50%',
      opacity,
      transform: `translate(${position.x}px, ${position.y}px)`,
      pointerEvents: 'none',
      left: -20,
      top: -20,
      width: 40,
      height: 40,
    }} />
  );
}

usePointerPosition.js

import { useState, useEffect } from 'react';

export function usePointerPosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  useEffect(() => {
    function handleMove(e) {
      setPosition({ x: e.clientX, y: e.clientY });
    }
    window.addEventListener('pointermove', handleMove);
    return () => window.removeEventListener('pointermove', handleMove);
  }, []);
  return position;
}

这个是 react的一个hook函数,作用就是根据当前计算更新当前鼠标的位置。
如果是一个新的页面且只有这个示例是正常的。
不过示例前面还用元素就会出现偏差。
解决方案是在外面包括一个相对定位的盒子,然后在这个相对定位盒子里监听鼠标移动事件,更新小球偏离位置。
所以,这需要react挂载后就立即在这个相对定位盒子里监听鼠标移动事件
在这里插入图片描述

解决方法

方法1 usePointerPosition.js hook传递dom参数。

参数box:这个代表使用useRef绑定的盒子dom对象
hook作用:在挂载后立即给box.current绑定鼠标移动事件,事件会更新position对像的值,最后返回。

function usePointerPosition(box){
  const [position,setPosition] = useState({x:0,y:0})

  useEffect(()=>{
  	  let dom = box.current
   
 
	  if(dom)
	  dom.addEventListener("mousemove",handleMove)
	  function handleMove(e){
	    // console.log(e.clientX,e.clientY)
	  setPosition({x:e.offsetX,y:e.offsetY});
	} 

  return ()=>{
      if(dom)
      dom.removeEventListener("mousemove",handleMove)
    }
  },[box])
  return position
}

改造好后,可以和以前一样使用,参数传递useRef的dom对象就行。
在这里插入图片描述

方法2. usePointerPosition.js hook传递回调函数参数。

使用时,直接在回调函数内,给dom盒子绑定鼠标移动事件

function usePointerPosition2(callback){
  const [position,setPosition] = useState({x:0,y:0})

  useEffect(()=>{
   // 回调函数传递移动事件要使用的函数,返回dom对象,
  let dom = callback(handleMove)
  
  function handleMove(e){
  setPosition({x:e.offsetX,y:e.offsetY});
} 
    return ()=>{
      if(dom)
      dom.removeEventListener("mousemove",handleMove)
    }
    //  报错很正常,因为使用callback依赖项,却没在下面绑定,下一句是规避掉代码检查
  // eslint-disable-next-line react-hooks/exhaustive-deps
  },[])
  return position
}

使用:

 let pos1 = usePointerPosition(
(handleMove)=>{
  console.log(11);
      box.current.addEventListener("mousemove",handleMove)
    return box.current
     }
);

方法3 使用useCallBack hook代替规避代码检查

官方是极其不推荐使用 注释 // eslint-disable-next-line react-hooks/exhaustive-deps规避代码检查的。
我们又确定callback的内容只变化一次,如果不使用注释,而将callback函数作为依赖项的话,将会执行多次callback函数,每次渲染都会执行,因为函数本身在渲染时在变。
这时 我们可以使用useCallback将callback包裹一下,缓存住,这样再渲染就是原来的函数。
useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。
在这里插入图片描述
改造 usePointerPosition.

function usePointerPosition3(callback){
  const [position,setPosition] = useState({x:0,y:0})

  useEffect(()=>{
   // 回调函数返回dom对象
  let dom = callback(handleMove)
  
  function handleMove(e){
  setPosition({x:e.offsetX,y:e.offsetY});
} 
    return ()=>{
      if(dom)
      dom.removeEventListener("mousemove",handleMove)
    }
    //  报错很正常,因为使用callback依赖项,却没在下面绑定,下一句是规避掉代码检查
  },[callback])
  return position
}```

使用

```javascript
import {useEffect,useState} from 'react'
import { useRef } from 'react';
import { useCallback } from "react";


const callback = useCallback(
    (handleMove)=>{
      console.log(11);
      box.current.addEventListener("mousemove",handleMove)
      return box.current
    }
  ,[box])
  let pos1 = usePointerPosition(callback);

这样callback就不会多次改变导致useEffect重复执行了。

结语。

在useEffect里操作dom是比较危险的。如果有其他更好的替代方案,请使用其他的。

更多推荐

CFCA证书 申请 流程(二)

关于CFCA证书的介绍,可参考上一篇文章:CFCA证书申请流程(一)_身价五毛的博客-CSDN博客CFCA测试证书申请流程测试证书主要用于在测试环境对所需功能进行验证,例如HTTPS访问等。首先,向CFCA的支持邮箱(support@cfca.com.cn)发送邮件,描述申请证书的类型和参数,具体包括:此邮件是自动回复

轻量云服务器租用好在哪

从技术上讲,轻量级云服务器是特化了某一配置的高性价比云服务器的结合。下面,我们将了解轻量级云服务器有什么优势,使用物理服务器搭建网站,您需要租用整个服务器,这成本会变得非常昂贵。这对于一些比较简单的使用需求而言,例如搭建一个单页网站或者一个做个代理的话其实用整台服务器不仅性能溢出而且价格很贵对于初学者来说,使用轻量级云

灾备系统中虚拟机的有代理备份与无代理备份之间的差异

虚拟机的有代理备份是在虚拟机内部安装备份代理程序,然后把虚拟机当作物理机一样来进行备份任务。借助虚拟机系统中内置的程序来进行备份的,就像在正常系统中备份那样,借助备份和还原(Windows7)功能对系统进行备份。但是这种方法操作起来比较麻烦,而且也没有办法进行批量化操作,比如有大量的虚拟机,都需要备份它们的数据,那么就

前后台分离开发 YAPI平台 前端工程化之Vue-cli

目录YAPI介绍前端工程化之Vue-cli前端工程化简介前端工程化入门——Vue-cli环境准备Vue项目简介创建Vue项目vue项目目录结构介绍vue项目运行方法Vue项目开发流程前后台混合开发这种开发模式有如下缺点:沟通成本高:后台人员发现前端有问题,需要找前端人员修改,前端修改成功,再交给后台人员使用分工不明确:

nginx反向代理vue项目

文章目录前言一、创建站点1.添加站点2.添加ssl证书二、反向代理vue项目1.添加反向代理2.更改vue项目配置3.修改反向代理配置前言项目描述:前端vue项目、后端Java项目、首页WordPress项目客户要求:使用宝塔进行部署需求描述:客户只有一个SSL单域名DV证书要求首页部署wordpress项目作为官网,

大话数据结构 2 算法

算法:算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作算法的五个基本特性:输入、输出、有穷性、确定性、可行性1.输入输出:算法具有0个或多个输入,算法至少有1个或多个输出。2.有穷性:指算法在执行有限的步骤后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成

JUC相关面试题

👏作者简介:大家好,我是爱发博客的嗯哼,爱好Java的小菜鸟🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦📝社区论坛:希望大家能加入社区共同进步🧑‍💼个人博客:智慧笔记📕系列专栏:面试宝典本文引自黑马程序员Java面试宝典JUC基础薄弱的可以先看JUC详解文章目录面试官:聊一下并行和并发有什么

干货:数据仓库基础知识(全)

1、什么是数据仓库?权威定义:数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。1)数据仓库是用于支持决策、面向分析型数据处理;2)对多个异构的数据源有效集成,集成后按照主题进行重组,并包含历史数据,而且存放在数据仓库中的数据一般不再修改。面对大数据的多样性,在存储和处理这些大数据

【算法】单调栈

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。推荐:kuan的首页,持续学习,不断总结,共同进步,活到老学到老导航檀越剑指大厂系列:全面总结java核心技术点,如集合,jvm,并发编程redis,kaf

JAVA开发技术能力和学历哪个重要?

Java开发技术能力和学历都是Java开发人员必须具备的基本条件,二者之间没有谁更加重要的绝对答案。不同的人在不同的时期,所处的不同环境下会给出不同的答案。下面从多个角度进行讨论,探究Java开发技术能力和学历哪个更重要。1、招聘角度从企业的招聘需求来看,技术能力在某些情况下更为重要。随着科技的不断发展,对于一些前沿技

8位微控制器上的轻量级SM2加密算法实现:C语言详细指南与完整代码解析

引言在当今的数字化世界中,安全性是每个系统的核心。无论是智能家居、医疗设备还是工业自动化,每个设备都需要确保数据的安全性和完整性。对于许多应用来说,使用高级的微控制器或处理器可能是不切实际的,因为它们可能会增加成本、功耗和尺寸。这就是为什么8位微控制器仍然在许多应用中占据着重要的地位。但是,8位微控制器的计算能力有限,

热文推荐