为何传递空依赖数组时,useEffect中注册的函数仍会执行?
问题分析与解答
首先明确核心逻辑:useEffect的空依赖数组只是让这个副作用函数仅在组件挂载时执行一次(也就是只注册一次事件监听器),并不是阻止事件触发后调用处理函数。只要监听器成功注册到window上,后续鼠标移动时,浏览器就会触发mousemove事件,自然会调用对应的处理函数——这是浏览器的事件机制,和组件是否重新渲染、useEffect是否再次执行完全无关。
再看你代码里的关键错误:window.addEventListener("onmousemove", handleMouseMove);
这里事件名写错了,addEventListener接受的是不带on前缀的事件名称,正确写法应该是"mousemove"。你当前的代码其实并没有成功注册原生事件,除非手动触发了onmousemove自定义事件,否则handleMouseMove不会被调用。如果实际运行中函数确实被调用了,大概率是你已经把事件名改成了正确的mousemove。
另外补充一个潜在问题:你的handleMouseMove定义在组件内部,每次组件渲染都会重新创建这个函数。虽然空依赖数组让useEffect只执行一次,但注册的是组件第一次渲染时创建的handleMouseMove——如果函数里用到了其他状态或props,就会出现闭包导致的旧值问题。解决这个问题可以用两种方式:
// 方式一:用useCallback包裹函数,确保引用稳定 import { useEffect, useState, useCallback } from "react"; const UseEffect1 = () => { const [X, setX] = useState(0); const [Y, setY] = useState(0); const handleMouseMove = useCallback((e) => { setX(e.clientX); setY(e.clientY); }, []); useEffect(() => { window.addEventListener("mousemove", handleMouseMove); // 务必添加清理函数,避免内存泄漏 return () => { window.removeEventListener("mousemove", handleMouseMove); }; }, [handleMouseMove]); return ( <div> <p>width {X} - height {Y}</p> </div> ); };
// 方式二:直接把函数定义在useEffect内部 useEffect(() => { const handleMouseMove = (e) => { setX(e.clientX); setY(e.clientY); }; window.addEventListener("mousemove", handleMouseMove); return () => { window.removeEventListener("mousemove", handleMouseMove); }; }, []);
最后提醒:一定要在useEffect的清理函数中移除事件监听器,否则组件卸载后监听器仍会存在,可能引发内存泄漏。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

