React useState Hook值无法传递到其他函数,状态始终为0怎么解决?
问题原因
- 闭包捕获旧值问题:你点击
Go按钮触发countDown时,函数内部的setInterval和checkAnswer会捕获当前渲染周期的count状态值。后续点击按钮更新count触发组件重渲染,生成的是新的函数作用域,已经启动的定时器闭包永远引用的是触发countDown那一刻的count值,如果点击Go时count是0,后续读取的count就永远是0。 - 拼写错误:
checkAnswer中判断条件用了未定义的resultat,实际定义的常量是result- 按钮的
onClick绑定的是小写的countdown,实际函数名是驼峰的countDown,会触发函数未定义报错
- 普通变量无持久化:
cd是普通let变量,组件每次重渲染都会被重置为5,倒计时的数值更新不会在渲染之间持久化。 - 不必要的DOM操作:React是状态驱动UI,不需要手动调用
getElementById修改DOM内容,直接通过状态绑定到JSX即可。 - 定时器未清理:
setInterval没有配置卸载清理逻辑,组件提前卸载时会触发内存泄漏和报错。
修复后的代码
import { useState, useEffect, useRef } from 'react' const Component = () => { const [count, setCount] = useState(0) const [cd, setCd] = useState(0) const [message, setMessage] = useState('') const intervalRef = useRef(null) // 用ref存储最新的count值,规避闭包问题 const countRef = useRef(count) const RESULT = 25 // 同步最新count到ref useEffect(() => { countRef.current = count }, [count]) const counterHandler = () => { setCount(prev => prev + 1) } const checkAnswer = () => { if (countRef.current > RESULT) { setMessage('Yess!') } else { setMessage('Wrong!!') setTimeout(() => { setMessage('') }, 2000) } } const countDown = () => { // 先清理之前的定时器,避免重复点击产生多个定时器 if (intervalRef.current) clearInterval(intervalRef.current) setCd(5) intervalRef.current = setInterval(() => { setCd(prev => { if (prev > 0) { return prev - 1 } else { clearInterval(intervalRef.current) checkAnswer() return 0 } }) }, 1000) } // 组件卸载时清理定时器,避免内存泄漏 useEffect(() => { return () => { if (intervalRef.current) clearInterval(intervalRef.current) } }, []) return ( <div> <h1>{count}</h1> <h4>{cd}</h4> <button onClick={counterHandler}>Push Me!</button> <button id='go-btn' onClick={countDown}>Go</button> <h4>{message}</h4> </div> ) }
修复说明
- 用
useRef存储最新的count值,ref在组件全生命周期内引用不变,定时器回调里读取countRef.current就能拿到最新的状态值,解决闭包问题 - 把
cd、message都改为useState状态,自动驱动UI更新,移除所有手动DOM操作 - 统一修正了所有拼写错误
- 用
useRef存储定时器id,增加了重复点击和组件卸载时的定时器清理逻辑,避免内存泄漏
内容的提问来源于stack exchange,提问作者Luciano M.
相关产品推荐
相关产品推荐

