React中socket.on监听器无法获取useState最新变量值如何解决
问题本质
该问题属于React函数组件常见的闭包陷阱,和按引用传递变量无关:
函数组件每次渲染都会生成独立的闭包环境,初始渲染阶段注册的socket事件监听器只会捕获当前渲染周期的modalOpened状态快照。后续modalOpened更新后,已注册的旧监听器仍然访问的是首次渲染时的变量值。你封装的返回状态的函数本身也属于旧闭包的一部分,因此只会返回旧值。
解决方案
方案1:使用useRef存储最新状态(推荐)
useRef返回的是可变引用对象,其current属性的更新不会被闭包捕获快照,始终可以拿到最新值:
import { useState, useRef, useEffect } from 'react'; const [modalOpened, setModalOpened] = useState(false); // 声明ref同步state的最新值 const modalOpenedRef = useRef(modalOpened); // 每次modalOpened变化时更新ref的值 useEffect(() => { modalOpenedRef.current = modalOpened; }, [modalOpened]); const [data, setData] = useState([]); const handleSetData = (data) => { // 读取ref的current属性获取最新值 !modalOpenedRef.current && setData(data); } // 监听器仅注册一次,组件卸载时清理即可 useEffect(() => { const eventHandler = (data) => handleSetData(data); socket.on("someevent", eventHandler); return () => socket.off("someevent", eventHandler); }, []);
该方案无需重复注册事件监听器,性能更好,适合状态依赖较多的场景。
方案2:随状态变化重新注册监听器
将事件注册逻辑放到useEffect中,把modalOpened加入依赖数组,每次modalOpened变化时自动清理旧监听器、注册新监听器,新监听器会捕获最新的状态值:
import { useState, useEffect } from 'react'; const [modalOpened, setModalOpened] = useState(false); const [data, setData] = useState([]); const handleSetData = (data) => { !modalOpened && setData(data); } // 依赖modalOpened,每次变化时重新注册监听器 useEffect(() => { const eventHandler = (data) => handleSetData(data); socket.on("someevent", eventHandler); // 清理上一次渲染注册的旧监听器 return () => socket.off("someevent", eventHandler); }, [modalOpened]);
该方案逻辑更直观,适合依赖项较少的场景。
内容的提问来源于stack exchange,提问作者Darkbound
相关产品推荐
相关产品推荐

