React中Timer子组件倒计时结束后如何传递状态更新父组件参数
解决方案
问题根因
- React 规定props为只读属性,子组件直接修改传入的props不会生效,也无法触发父组件状态更新和重渲染
- 现有Timer组件仅接收了状态值,没有接收父组件的状态更新回调,无法反向通知父组件修改状态
- 原有倒计时结束判断使用
difference === 0,受定时器精度影响可能出现漏触发的情况
修改步骤
1. 修改Timer组件,新增过期回调
调整组件入参,删除没用的isValid属性,新增onExpire回调用于通知父组件倒计时结束,同时优化结束判断逻辑:
import React, { useEffect, useState } from "react"; import "./Timer.scss"; interface ITimerProps { // 倒计时结束回调 onExpire: () => void; } export const Timer = ({ onExpire }: ITimerProps) => { const [minutes, setMinutes] = useState(0); const [seconds, setSeconds] = useState(0); const tokenTTL = 10; useEffect(() => { const actualTime = new Date(); const target = new Date( actualTime.setMinutes(actualTime.getMinutes() + tokenTTL) ); const interval = setInterval(() => { const now = new Date(); const difference = target.getTime() - now.getTime(); // 倒计时结束处理 if (difference <= 0) { clearInterval(interval); onExpire(); setMinutes(0); setSeconds(0); return; } let m: string | number = Math.floor( (difference % (1000 * 60 * 60)) / (1000 * 60) ); m = m < 10 ? "0" + m : m; setMinutes(m as number); let s: string | number = Math.floor((difference % (1000 * 60)) / 1000); s = s < 10 ? "0" + s : s; setSeconds(s as number); }, 1000); return () => clearInterval(interval); }, [onExpire]); return ( <div className="Timer"> <div className="Timer__Container"> <div className="Timer-content"> <span className="minutes">{minutes}</span> </div> <span className="divider">:</span> <div className="Timer-content"> <span className="seconds">{seconds}</span> </div> </div> </div> ); };
2. 调整useLoginByCode自定义hook,暴露状态修改方法
在hook内部把isCodeExpired对应的修改方法暴露出来,同时刷新验证码的时候重置过期状态:
export const useLoginByCode = () => { // 原有逻辑保留 const [isCodeExpired, setIsCodeExpired] = useState(false); const refreshCode = () => { // 原有刷新验证码逻辑保留 setIsCodeExpired(false); // 刷新验证码时重置过期状态 } return { // 原有返回的字段保留 isCodeExpired, setIsCodeExpired, // 新增暴露状态修改方法 refreshCode, // 其他返回字段保留 } }
3. 调整Login父组件调用逻辑
解构获取状态修改方法,给Timer组件传递过期回调:
// 解构时新增setIsCodeExpired const { code, refreshCode, isCodeExpired, setIsCodeExpired, refresh, error, loading } = useLoginByCode();
渲染Timer的部分调整为:
{/* 新增key属性,每次code刷新时自动重启倒计时 */} <Timer onExpire={() => setIsCodeExpired(true)} key={code} />
额外优化说明
- 给Timer加
key={code}属性,每次验证码刷新时组件自动重新挂载,无需额外编写重置倒计时的逻辑 - 倒计时结束后主动清除定时器,避免无用的定时器继续运行占用资源
内容的提问来源于stack exchange,提问作者shivetay
相关产品推荐
相关产品推荐

