React中useState在useEffect/事件监听内无法更新值的问题
React滚动监听中useState状态不更新的问题解决
问题原因
- 闭包陷阱:你的
useEffect依赖数组是空的,回调只会在组件挂载时执行一次。此时事件监听器捕获的scrollSection是初始值0,后续即使状态更新,监听器里拿到的还是这个旧值,所以你打印的scrollSection永远是0,看起来像状态没更新。 - 监听移除失效:你在
addEventListener和removeEventListener里用了不同的箭头函数(每次都是新创建的),导致组件卸载时无法正确移除滚动监听,会造成内存泄漏。 - 状态更新的即时性:
setScrollSection是异步的,而且你打印的是传入的旧scrollSection参数,即使状态更新了,这里也看不到最新值。
修复方案
方案1:使用函数式更新+稳定的监听回调
把事件回调定义在useEffect内部,用函数式更新获取最新状态,避免依赖scrollSection:
function SideNav(props: any) { const [scrollSection, setScrollSection] = React.useState(0); React.useEffect(() => { const handleScroll = () => { const scrollY = window.scrollY; // 函数式更新,拿到最新的状态值 setScrollSection(prev => { // 这里替换成你的实际判断逻辑,比如根据scrollY切换section if (scrollY > prev) { return 1; } return prev; }); console.log("scroll Y: "+scrollY); }; window.addEventListener('scroll', handleScroll); // 移除监听时用同一个handleScroll函数 return () => window.removeEventListener('scroll', handleScroll); }, []); }
方案2:复用函数+正确设置依赖
如果checkSection需要复用,可以用useCallback包裹,同时正确设置依赖:
function SideNav(props: any) { const [scrollSection, setScrollSection] = React.useState(0); const checkSection = React.useCallback(() => { const scrollSections = [0, 1538, 2583, 2089, 3089]; const scrollY = window.scrollY; setScrollSection(1); console.log("scroll Y: "+scrollY); }, []); React.useEffect(() => { window.addEventListener('scroll', checkSection); return () => window.removeEventListener('scroll', checkSection); }, [checkSection]); // 单独监听scrollSection变化,打印最新值 React.useEffect(() => { console.log("最新section: "+scrollSection); }, [scrollSection]); }
额外优化建议
滚动监听触发频率很高,建议加节流处理避免性能问题:
// 自己实现简单节流函数 function throttle(func: Function, delay: number) { let lastCall = 0; return (...args: any[]) => { const now = Date.now(); if (now - lastCall >= delay) { func(...args); lastCall = now; } }; } // 在useEffect里使用 const handleScroll = React.useCallback(throttle(() => { // 你的滚动逻辑 }, 100), []);
内容的提问来源于stack exchange,提问作者Darcy Power
相关产品推荐
相关产品推荐

