React中setState与interval配合时currentExpVal始终为0,定时器无法停止
问题原因与解决办法
为什么currentExpVal始终是0?
你的useEffect依赖项是空数组[],这会让它只在组件首次渲染时执行一次。定时器的回调函数会形成闭包,捕获的是组件初始状态下的currentExpVal(也就是0)。
后续虽然通过setCurrentExpVal((prev) => prev + 1)正确累加了状态值,但定时器回调里的currentExpVal永远是闭包里保存的那个初始值0。所以currentExpVal < expValueRef.current的判断永远为真,永远不会触发清除定时器的逻辑。
修复方案
方案1:用Ref跟踪最新状态值
新增一个Ref来同步currentExpVal的最新值,这样定时器里能直接拿到当前的状态进行判断:
import React, { useEffect, useRef, useState } from "react"; import styles from "./Experience.module.scss"; type Props = {}; export const Experience: React.FC<Props> = (props: Props) => { const expValueRef = useRef(10); const [currentExpVal, setCurrentExpVal] = useState(0); const currentExpRef = useRef(currentExpVal); // 同步状态到Ref,确保Ref始终持有最新值 useEffect(() => { currentExpRef.current = currentExpVal; }, [currentExpVal]); useEffect(() => { const intervalRef = setInterval(() => { if (currentExpRef.current < expValueRef.current) { console.info(currentExpRef.current, expValueRef.current); setCurrentExpVal((prev) => prev + 1); } else { console.info("clear"); clearInterval(intervalRef); } }, 1500); // 组件卸载时清理定时器,防止内存泄漏 return () => clearInterval(intervalRef); }, []); return ( <div className={styles["exp-wrapper"]}> {currentExpVal} </div> ); };
方案2:给useEffect添加依赖并清理旧定时器
让useEffect依赖currentExpVal,每次状态更新时重新创建定时器,但要记得清理旧的定时器,避免多个定时器同时运行:
import React, { useEffect, useRef, useState } from "react"; import styles from "./Experience.module.scss"; type Props = {}; export const Experience: React.FC<Props> = (props: Props) => { const expValueRef = useRef(10); const [currentExpVal, setCurrentExpVal] = useState(0); useEffect(() => { // 已经达到目标值时,不再创建定时器 if (currentExpVal >= expValueRef.current) return; const intervalRef = setInterval(() => { setCurrentExpVal((prev) => { // 在下一次更新前判断,如果即将达到目标,就清除定时器 const nextVal = prev + 1; if (nextVal >= expValueRef.current) { clearInterval(intervalRef); } return nextVal; }); }, 1500); // 清理旧定时器 return () => clearInterval(intervalRef); }, [currentExpVal]); return ( <div className={styles["exp-wrapper"]}> {currentExpVal} </div> ); };
内容的提问来源于stack exchange,提问作者Dmytro Khilchuk
相关产品推荐
相关产品推荐

