使用useEffect、useState、useInView实现元素进入视口时数字递增效果
问题原因
- 闭包陷阱:负责执行计数的
useEffect仅依赖counting变量,仅在counting变为true时执行一次,回调内部捕获的number永远是初始值0,每次执行setNumber(number + 1)实际都是将数值设置为1,定时器后续触发时拿到的仍然是旧的number值,导致数值无法正常递增。 - 缺少定时器清理逻辑:
setInterval启动后没有在数值达到目标值、组件卸载时销毁,既会造成内存泄漏,也会导致定时器持续无意义运行。 - 状态更新写法错误:直接使用闭包中的
number变量做计算,没有使用React状态更新的函数式写法,无法拿到最新的状态值。
修复方案
import { useState, useEffect } from 'react'; import { useInView } from 'react-intersection-observer'; function NumericIndicator(props) { const [number, setNumber] = useState(0); const [ref, inView] = useInView({ triggerOnce: true // 配置进入视口后只触发一次,不需要重复计数可保留该配置 }); useEffect(() => { // 未进入视口或数值已达目标时不启动定时器 if (!inView || number >= props.value) return; const timer = setInterval(() => { setNumber(prev => { // 函数式更新直接拿到最新的状态值,规避闭包问题 if (prev < props.value) { return prev + 1; } // 达到目标值后清除定时器 clearInterval(timer); return prev; }); }, 50); // 调整该数值可控制计数速度,值越小速度越快 // 依赖变化/组件卸载时自动清理定时器 return () => clearInterval(timer); }, [inView, number, props.value]); return ( <div ref={ref}> <h1>{number}</h1> <p> <small>需要实现从0递增到{props.value}的效果</small> </p> </div> ); }
内容的提问来源于stack exchange,提问作者RBFraphael
相关产品推荐
相关产品推荐

