React Hooks中如何根据页面URL手动移除滚动事件监听器?
如何正确在React Hook中根据URL移除滚动事件监听器
嘿,这个问题我之前也踩过坑!你当前代码的问题在于:每次useEffect执行时,都会重新生成一个全新的handleScrollEvent函数实例。当URL变化进入else分支调用removeEventListener时,你传入的是这次渲染刚创建的新函数,和之前添加监听时用的旧函数完全不是同一个引用,浏览器自然找不到对应的监听器,所以移除操作无效。
给你两种可靠的解决方案,按需选择:
方案一:利用useEffect的清理函数(推荐,更简洁)
这种写法不需要额外的Hook,只需要把函数定义在useEffect内部,然后通过清理函数统一处理移除逻辑:
useEffect(() => { function handleScrollEvent() { setHeaderIsVisible(window.scrollY > 100); } // 仅在首页时添加滚动监听 if (props.location.pathname === "/") { window.addEventListener("scroll", handleScrollEvent, true); } // 清理函数:当URL变化或组件卸载时自动执行 return () => { window.removeEventListener("scroll", handleScrollEvent, true); }; }, [props.location.pathname]);
为什么这个能行?
因为每次useEffect执行时,handleScrollEvent和对应的清理函数处于同一个作用域,它们引用的是同一个函数实例。当URL变化时,旧的useEffect会先触发清理函数,用正确的引用移除之前的监听器,然后新的useEffect再根据新URL决定是否添加新的监听,完美避免了引用不匹配的问题。
方案二:用useCallback缓存函数引用
如果你需要在useEffect外部复用这个滚动处理函数,可以用useCallback缓存它的引用,确保每次渲染时函数引用稳定:
import { useEffect, useCallback } from 'react'; // ... // 用useCallback缓存函数,依赖数组留空(setHeaderIsVisible是useState返回的稳定函数,不需要加进依赖) const handleScrollEvent = useCallback(() => { setHeaderIsVisible(window.scrollY > 100); }, []); useEffect(() => { if (props.location.pathname === "/") { window.addEventListener("scroll", handleScrollEvent, true); } return () => { window.removeEventListener("scroll", handleScrollEvent, true); }; }, [props.location.pathname, handleScrollEvent]);
注意点
不管用哪种方案,添加和移除监听器时的第三个参数(true,表示捕获阶段)必须完全一致,否则浏览器也会认为是不同的监听器,导致移除失败。
内容的提问来源于stack exchange,提问作者Hadi Ranjbar
相关产品推荐
相关产品推荐

