React无父子关系函数组件如何跨组件调用方法重启倒计时
跨平级组件触发倒计时重启实现方案
react-countdown-circle-timer 本身不暴露外部调用的重启API,官方推荐的重启逻辑是:给组件传入动态变化的key属性,当key值更新时组件会自动重挂载,直接实现倒计时重置重启。
针对两个无直接父子关系的平级组件通信,有两种无兼容问题的实现方案:
方案一:状态提升(React官方推荐,无额外依赖)
把控制重启的状态放到两个组件的最近共同父组件中维护,再把状态和触发方法分别下发给两个子组件,符合React单向数据流设计原则。
1. 修改共同父组件
在两个组件共同挂载的父级文件中,定义计时器key状态和重启方法:
import { useState } from 'react'; import CountDownTimer from './timer'; import WiningCondition from './winingcondition'; const CommonParent = () => { // 计时器key,值变化时自动触发计时器重启 const [timerKey, setTimerKey] = useState(0); // 重启方法,传给胜负判定组件调用 const restartTimer = () => { setTimerKey(prev => prev + 1); }; return ( <div> <CountDownTimer timerKey={timerKey} /> <WiningCondition onRestartTimer={restartTimer} /* 原有其他props保持不变 */ /> </div> ); }; export default CommonParent;
2. 修改timer.js
给计时器组件绑定传入的key,同时修复原有代码的小问题(拼写错误、色值多余分号):
import { CountdownCircleTimer } from "react-countdown-circle-timer"; const renderTime = ({ remainingTime }) => { if (remainingTime === 0) { return <div className="timer">Too late...</div>; } return ( <div className="timer font-family flex flex--column align__items--center"> <div className="value font-size--80 font text--white "> {remainingTime} </div> </div> ); }; const CountDownTimer = ({ timerKey }) => { return ( <div className="App"> <div className="timer-wrapper flex justify__content--center bg-grad--timer b-radius--circle"> <CountdownCircleTimer key={timerKey} isPlaying duration={30} stroke={"white"} colors={["#FFAD22", "#FFFFFF", "#FFFFFF", "#A30000"]} colorsTime={[25, 20, 15, 0]} onComplete={() => { return { shouldRepeat: true, delay: 0 }; }} > {renderTime} </CountdownCircleTimer> </div> </div> ); }; export default CountDownTimer;
3. 修改winingcondition.js
在胜负判定逻辑触发时,调用父组件传入的重启方法:
const checkValidation = (isValidation, userAnswer) => { // 判定触发时直接调用重启方法 props.onRestartTimer(); if (isValidation) { props.handleWinFunc(); Swal.fire({ title: "Good job!", icon: "success", timer: 1000, showConfirmButton: false, backdrop: ` rgba(0,0,123,0.4) url("https://acegif.com/wp-content/gif/confetti-12.gif") top no-repeat `, }); } else { props.handleWinFunc(); Swal.fire({ title: "Wrong Answer", html: `<b>Correct answer:${correctAns}</b>`, icon: "error", }); } setarrayValue([]); };
方案二:轻量事件总线(适合组件层级跨度大的场景)
如果两个组件的共同父组件层级很高,层层传递props过于繁琐,可以实现一个极简事件总线完成跨组件通信,不需要引入第三方库。
1. 新建eventBus.js
const eventBus = { eventMap: {}, on(eventName, cb) { if (!this.eventMap[eventName]) this.eventMap[eventName] = []; this.eventMap[eventName].push(cb); }, off(eventName, cb) { if (!this.eventMap[eventName]) return; this.eventMap[eventName] = this.eventMap[eventName].filter(item => item !== cb); }, emit(eventName, payload) { if (!this.eventMap[eventName]) return; this.eventMap[eventName].forEach(cb => cb(payload)); } }; export default eventBus;
2. 修改timer.js
在组件内监听重启事件,更新key值:
import { useState, useEffect } from "react"; import { CountdownCircleTimer } from "react-countdown-circle-timer"; import eventBus from './eventBus'; // renderTime逻辑和之前保持一致 const CountDownTimer = () => { const [timerKey, setTimerKey] = useState(0); useEffect(() => { const handleRestart = () => setTimerKey(prev => prev + 1); eventBus.on('restart-timer', handleRestart); // 组件卸载时移除监听,避免内存泄漏 return () => eventBus.off('restart-timer', handleRestart); }, []); return ( <div className="App"> <div className="timer-wrapper flex justify__content--center bg-grad--timer b-radius--circle"> <CountdownCircleTimer key={timerKey} // 其余props配置和之前一致 isPlaying duration={30} stroke={"white"} colors={["#FFAD22", "#FFFFFF", "#FFFFFF", "#A30000"]} colorsTime={[25, 20, 15, 0]} onComplete={() => ({ shouldRepeat: true, delay: 0 })} > {renderTime} </CountdownCircleTimer> </div> </div> ); }; export default CountDownTimer;
3. 修改winingcondition.js
判定触发时直接发送重启事件即可,不需要父组件传递方法:
import eventBus from './eventBus'; const checkValidation = (isValidation, userAnswer) => { // 触发全局重启事件 eventBus.emit('restart-timer'); // 原有胜负判定、弹窗逻辑保持不变 if (isValidation) { props.handleWinFunc(); // 胜利弹窗逻辑 } else { props.handleWinFunc(); // 失败弹窗逻辑 } setarrayValue([]); };
如果项目已经接入Redux、Zustand、Jotai等全局状态管理库,直接把
timerKey存入全局store即可,逻辑和状态提升完全一致,不需要额外实现事件总线。
内容的提问来源于stack exchange,提问作者Chaudhari Devam
相关产品推荐
相关产品推荐

