React函数式组件中如何在函数内访问更新后的state?
问题原因
你的问题出在闭包陷阱上:useEffect的依赖项是空数组[],这意味着它只在组件挂载时执行一次。此时readNum回调捕获的是初始状态的num(值为0),后续num通过setNum更新后,这个回调不会重新创建,所以始终读取的是最初的闭包值。
解决方案
方法1:用useRef保存最新的num值
通过useRef维护一个始终指向最新num的引用,事件回调里直接读取这个引用就能拿到最新值:
import {useEffect, useRef, useState} from "react"; import {EventEmitter} from "events"; const NumComponent = () => { const [num, setNum] = useState(0) const numRef = useRef(num); // 每次num更新时同步更新ref的内容 useEffect(() => { numRef.current = num; }, [num]); const emitter = new EventEmitter() const emitterRef = useRef(emitter) useEffect(() => { emitterRef.current.on('setNum', () => setNum(10)) // 读取ref里的最新num emitterRef.current.on('readNum', () => console.log(numRef.current)) }, []) return <div> {num} <div> <button onClick={() => emitterRef.current.emit('setNum')}>set num</button> </div> <div> <button onClick={() => emitterRef.current.emit('readNum')}>read num</button> </div> </div> } export default NumComponent
方法2:更新useEffect的依赖项
把num加入useEffect的依赖数组,这样每次num变化时,都会重新绑定事件回调,确保回调能获取最新的num。注意要添加清理函数移除旧监听,避免内存泄漏:
import {useEffect, useRef, useState} from "react"; import {EventEmitter} from "events"; const NumComponent = () => { const [num, setNum] = useState(0) const emitter = new EventEmitter() const emitterRef = useRef(emitter) useEffect(() => { const handleSetNum = () => setNum(10); const handleReadNum = () => console.log(num); emitterRef.current.on('setNum', handleSetNum) emitterRef.current.on('readNum', handleReadNum) // 组件更新或卸载时移除旧监听 return () => { emitterRef.current.off('setNum', handleSetNum) emitterRef.current.off('readNum', handleReadNum) } }, [num]) // 依赖num,更新时重新绑定回调 return <div> {num} <div> <button onClick={() => emitterRef.current.emit('setNum')}>set num</button> </div> <div> <button onClick={() => emitterRef.current.emit('readNum')}>read num</button> </div> </div> } export default NumComponent
方法3:函数式更新获取最新值(适用于需要修改num的场景)
如果你的readNum回调不仅要读取还要修改num,可以用setNum的函数式形式,它能直接拿到当前最新的状态值:
// 示例:readNum时打印最新num并加1 emitterRef.current.on('readNum', () => { setNum(prevNum => { console.log(prevNum); // 这里能拿到最新的num值 return prevNum + 1; }) })
总结
单纯读取最新值选方法1或2都可以;如果要在回调里修改状态,方法3的函数式更新更直接。
内容的提问来源于stack exchange,提问作者Kapicka
相关产品推荐
相关产品推荐

