You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React类组件转带Hooks的函数组件:事件监听器缩放异常问题

问题根因

核心是闭包陷阱导致的state值被锁死:

  • 你写的useEffect依赖项为空数组,只会在组件首次挂载时执行一次,绑定的handleScroll是首次渲染时生成的版本
  • 首次渲染时size的初始值为30,这个值被闭包捕获后,后续所有handleScroll触发时读取的size永远是30,不会跟着state更新
    所以你每次滚动的表现就是:
  • 向外滚:判断30<50,设置size=32,下次再滚还是判断30<50,还是设置成32,看起来就只能动一次
  • 向内滚同理,最多到28就无法继续缩小

修复方案

方案1:给钩子补全依赖项

给useCallback和useEffect补全对应依赖,保证每次size变化后,绑定的事件处理函数都是最新版本:

// 给useCallback加size依赖
const handleScroll = useCallback((event) => {
    if (event.deltaY > 0 && size < 50) {
        setSize(size + 2)
    } else if (event.deltaY < 0 && size > 20) {
        setSize(size - 2)
    } 
}, [size]) 

// 给useEffect加handleScroll依赖,增加边界判断避免ref为空报错
useEffect(() => {
    if(!zoomTarget.current) return
    zoomTarget.current.addEventListener('wheel', handleScroll, true); 
    return() => {zoomTarget.current.removeEventListener('wheel', handleScroll, true)}
},[handleScroll]);

方案2:使用setSize函数式更新(更推荐)

直接在setSize里用函数式写法读取最新的state值,不需要依赖外部的size,不需要频繁解绑重绑事件,性能更好:

const handleScroll = useCallback((event) => {
    setSize(prevSize => { // 直接读取上一次的最新size值
        if (event.deltaY > 0 && prevSize < 50) {
            return prevSize + 2
        } else if (event.deltaY < 0 && prevSize > 20) {
            return prevSize - 2
        }
        return prevSize // 不满足缩放条件就返回原尺寸
    })
}, []) // 不需要加size依赖

useEffect(() => {
    if(!zoomTarget.current) return
    zoomTarget.current.addEventListener('wheel', handleScroll, true); 
    return() => {zoomTarget.current.removeEventListener('wheel', handleScroll, true)}
},[handleScroll]);

内容的提问来源于stack exchange,提问作者Ettur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 02:36:04