useState未更新组件属性:react-countdown-circle-timer重置倒计时失效问题
问题原因
- 重置逻辑错误:你在
resetHandler中执行setTotalDuration(totalDuration)属于给state赋完全相同的值,React会跳过本次状态更新,不会触发组件重渲染。你给倒计时组件绑定的key属性值没有变化,组件不会重新初始化,自然不会重置计时。 - useEffect依赖不合理:当前useEffect监听整个
props对象,任意props属性变化都会触发时长重新计算,性能开销大,实际只需要监听你用到的props.item.red即可。 - 缺少稳定的初始时长存储:你没有单独存储初始的倒计时总时长,运行过程中如果
totalDuration被修改,无法直接回到初始值。
解决方案
步骤1:新增两个状态
分别存储初始倒计时时长、倒计时组件的重置标识:
// 存储初始倒计时总时长 const [initialDuration, setInitialDuration] = useState(0); // 倒计时组件重置key,每次更新都会触发组件重新挂载 const [resetKey, setResetKey] = useState(0);
步骤2:修正useEffect逻辑与依赖
useEffect(() => { const myDuration = parseInt(props.item.red.split(":")[0]) * 60 + parseInt(props.item.red.split(":")[1]) + 30; // 同时更新初始时长和当前运行时长 setInitialDuration(myDuration); setTotalDuration(myDuration); // 只监听用到的props属性 }, [props.item.red]);
步骤3:修正重置逻辑
每次点击重置按钮时更新重置key,触发倒计时组件重新挂载,自动回到初始时长:
const resetHandler = () => { setTimerOn(false); // 更新重置key,触发倒计时组件重新初始化 setResetKey(prev => prev + 1); };
如果你需要点击暂停(停止)按钮时同时重置,只需要把上面的重置key更新逻辑加到stopHandler里即可。
步骤4:修改倒计时组件的key属性
<CountdownCircleTimer // 绑定重置key key={resetKey} isPlaying={timerOn} duration={initialDuration} // 其余属性保持不变 ... >
完整修改后代码
import { Card, Col, Row } from "react-bootstrap"; import { CountdownCircleTimer } from "react-countdown-circle-timer"; import { useEffect, useState } from "react"; // 注意:代码中用到的classes需要确保你已经正确引入了样式文件,比如import classes from './xxx.module.css' const formatRemainingTime = (time) => { const minutes = Math.floor((time % 3600) / 60); let seconds = time % 60; if (seconds < 10 && seconds > -1) { seconds = `0${seconds}`; } return `${minutes}:${seconds}`; }; const renderTime = ({ remainingTime }) => { return ( <div className={classes.timer}> <div className={classes.text}>Remaining</div> <div className={classes.value}>{formatRemainingTime(remainingTime)}</div> <div className={classes.text}></div> </div> ); }; function MainItem(props) { const [totalDuration, setTotalDuration] = useState(0); const [timerOn, setTimerOn] = useState(false); // 新增状态 const [initialDuration, setInitialDuration] = useState(0); const [resetKey, setResetKey] = useState(0); useEffect(() => { const myDuration = parseInt(props.item.red.split(":")[0]) * 60 + parseInt(props.item.red.split(":")[1]) + 30; setInitialDuration(myDuration); setTotalDuration(myDuration); }, [props.item.red]); const startHandler = () => { setTimerOn(true); }; const stopHandler = () => { setTimerOn(false); // 如果需要点击停止就重置,放开下面一行注释 // setResetKey(prev => prev + 1); }; const resetHandler = () => { setTimerOn(false); setResetKey(prev => prev + 1); }; return ( <Row> <Col> <Card> <Card.Body> <Row> <Col> <div> <button onClick={startHandler}>启动</button> <button onClick={stopHandler}>暂停</button> <button onClick={resetHandler}>重置</button> </div> </Col> <Col className={classes.timer_wrapper}> <CountdownCircleTimer key={resetKey} isPlaying={timerOn} duration={initialDuration} size={300} strokeWidth={20} trailColor={"#363537"} colors={[ ["#42b0f5", 0.5], ["#42b0f5", 0.01], ["#2bb32b", 0.2], ["#2bb32b", 0.01], ["#eeee60", 0.2], ["#eeee60", 0.01], ["#ff4a2e"], ]} onComplete={() => [true, 1000]} > {renderTime} </CountdownCircleTimer> </Col> </Row> </Card.Body> </Card> </Col> </Row> ); } export default MainItem;
内容的提问来源于stack exchange,提问作者user2484249
相关产品推荐
相关产品推荐

