为何setInterval会导致progressValue超过1?
进度值超过1的原因及修复方案
核心原因
1. 闭包捕获旧状态导致判断失效
第一个useEffect的依赖数组是空的,这意味着它内部的progressValue永远是组件第一次渲染时的初始值0。哪怕后续progressValue已经被更新到1,定时器回调里的if (progressValue < 1)判断始终为true,会持续执行setProgressValue((t) => t + 0.3),最终让进度值突破1的上限。
2. 竞态条件导致定时器清理不彻底
虽然在axios请求完成后调用了clearInterval(intervalId),但如果网络请求完成的时机刚好卡在定时器回调执行的间隙,可能会出现:请求已经把progressValue设为1,但下一次定时器回调还是会执行,把值再加0.3变成1.3。
修复方案
方案一:给定时器Effect添加正确依赖
把progressValue加入依赖数组,这样每次状态更新后,定时器会重新创建,判断条件也会使用最新的状态值:
useEffect(() => { intervalId = setInterval(() => { if (progressValue < 1) { setProgressValue((t) => t + 0.3); } }, 100); return () => clearInterval(intervalId); }, [progressValue]);
方案二:在状态更新时直接做边界限制
不需要依赖progressValue,而是在函数式更新里直接把值限制在1以内,从根源避免超界:
useEffect(() => { intervalId = setInterval(() => { setProgressValue((t) => { const nextVal = t + 0.3; return nextVal > 1 ? 1 : nextVal; }); }, 100); return () => clearInterval(intervalId); }, []);
方案三:用useRef存储定时器ID(更稳妥)
全局变量intervalId可能在组件多次挂载/卸载时出现问题,改用useRef存储定时器ID,确保清理准确:
import "./styles.css"; import axios from "axios"; import { useState, useEffect, useRef } from "react"; export default function App() { const [progressValue, setProgressValue] = useState(0); const intervalRef = useRef(null); useEffect(() => { intervalRef.current = setInterval(() => { setProgressValue((t) => Math.min(t + 0.3, 1)); }, 100); return () => clearInterval(intervalRef.current); }, []); useEffect(() => { axios .get("https://randomuser.me/api/?results=5") .then((value) => { clearInterval(intervalRef.current); setProgressValue(1); }) .catch((error) => { clearInterval(intervalRef.current); setProgressValue(1); }); }, []); console.log("progressValue =>", progressValue); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }
内容的提问来源于stack exchange,提问作者Morton
相关产品推荐
相关产品推荐

