React中如何在useEffect内清理卸载函数 解决定时器残留跳转问题
修复方案
核心问题原因
每次调用test函数都会创建独立的setInterval和setTimeout实例,旧实例没有被主动销毁就会并行运行,到期后仍然触发跳转loser页面的逻辑。
修复后完整代码
import { useState, useEffect, useRef } from 'react' // 存储定时器引用,重渲染不会重置 const timerRef = useRef({ intervalId: null, timeoutId: null }) // 统一清理定时器逻辑 const clearTimers = () => { if (timerRef.current.intervalId) clearInterval(timerRef.current.intervalId) if (timerRef.current.timeoutId) clearTimeout(timerRef.current.timeoutId) timerRef.current = { intervalId: null, timeoutId: null } } const [state, setState] = useState({ haveTime: 10 }) const [states] = useState({ correct: "question", step: "loser" }) const test = (timer) => { // 执行新定时器前先清理上一轮所有定时器 clearTimers() let haveTime = 10 timerRef.current.intervalId = setInterval(() => { haveTime -= 1; setState({ haveTime }) }, 1000); timerRef.current.timeoutId = setTimeout(() => { clearInterval(timerRef.current.intervalId) dispatch(getNameStep(states.step)) clearTimers() }, timer); } const correectAnswer = () => { if (index === 9) { clearTimers() dispatch(getNameStep(stateForWinner.step)) } else { dispatch({ type: "indexIncrease" }) test(10000) } } let { question, correct_answer } = details.question[index]; useEffect(() => { test(10000) // 组件卸载时自动清理所有定时器 return clearTimers }, [])
关键修改说明
- 用
useRef存储定时器ID:普通变量会随组件重渲染重置,useRef存储的引用可以跨渲染周期稳定存在,能随时拿到上一轮的定时器实例 - 新增统一清理函数:每次调用
test前先销毁旧定时器,保证同一时间只有一组定时器在运行 - 补充边界场景清理:答对最后一题、组件卸载时都会主动清理定时器,避免内存泄漏和多余逻辑触发
内容的提问来源于stack exchange,提问作者mahdi kamran
相关产品推荐
相关产品推荐

