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
相关产品推荐
相关产品推荐

