使用window.setInterval、useEffect和useState更新值失败求助
React计时器中modelResult始终为0的修复方案
我正在模拟一个依赖计时器值的模型结果计时器,期望每秒更新计时器的同时更新模型值。相关代码如下:
"use client"; import { useState, useEffect, useRef } from "react"; function calculateDuration(startTimestamp) { const currentDate = new Date(); const timestamp = currentDate.getTime(); const duration = timestamp - startTimestamp; return Math.round(duration / 1000); } function calculateModelResult(duration) { return duration * 0.1; } export default function Page() { const startTime = 1692699200000; const [duration, setDuration] = useState(0); const [modelResult, setModelResult] = useState(0); const testTimeRef = useRef(0); useEffect(() => { testTimeRef.current = window.setInterval(() => { setDuration(calculateDuration(startTime)); setModelResult(calculateModelResult(duration)); }, 1000); }, []); return ( <h1> Hello, the duration is {duration}, the model result based on the current time is {modelResult} </h1> ); }
但运行后发现,duration能正常更新,modelResult却始终保持为0,运行效果如下:
Hello, the duration is 80188, the model result based on the current time is 0
Hello, the duration is 80196, the model result based on the current time is 0
问题原因
问题出在闭包捕获的状态值陈旧。useEffect的依赖数组为空,只会在组件挂载时执行一次,里面的定时器回调捕获的是初始的duration值(0),之后每次调用setModelResult时,都是基于这个初始值计算,结果自然一直是0。
修复方案
方案一:直接使用最新计算的duration值
在定时器回调里先计算出当前最新的duration,再用这个值同时更新两个状态,避开闭包的问题:
useEffect(() => { testTimeRef.current = window.setInterval(() => { const currentDuration = calculateDuration(startTime); setDuration(currentDuration); setModelResult(calculateModelResult(currentDuration)); }, 1000); // 组件卸载时清理定时器,避免内存泄漏 return () => clearInterval(testTimeRef.current); }, []);
方案二:监听duration变化同步更新modelResult
单独加一个依赖duration的useEffect,当duration更新时自动计算并更新modelResult:
useEffect(() => { testTimeRef.current = window.setInterval(() => { setDuration(calculateDuration(startTime)); }, 1000); return () => clearInterval(testTimeRef.current); }, []); // 当duration更新时,自动计算并更新modelResult useEffect(() => { setModelResult(calculateModelResult(duration)); }, [duration]);
这两种方案都能解决问题,方案一更直接,方案二更符合React状态更新的逻辑,可根据实际场景选择。
内容的提问来源于stack exchange,提问作者TungTung
相关产品推荐
相关产品推荐

