React函数组件嵌套函数内更新state总取初始值该如何修复?
问题根因
这是React函数组件典型的闭包陷阱问题:
- 用于初始化websocket的
useEffect依赖为空数组,只会在组件首次挂载时执行1次connectWebsocket方法 - 此时创建的
ws.onmessage回调捕获的是首次渲染上下文里的listState,也就是初始值[],后续组件重渲染时,这个已经绑定的回调永远拿不到更新后的listState - 每次调用
setListState([...listState, data])时,本质都是用初始的空数组拼接新数据,所以listState永远只会保留最后一条收到的消息,对应出现监听useEffect始终打印空数组的表现
修复方案
方案1:使用state的函数式更新(优先推荐)
setState支持传入函数作为参数,函数的入参会自动拿到最新的state值,不需要依赖外部上下文的state变量,仅修改ws.onmessage里的更新逻辑即可:
ws.onmessage = (event) => { let data = JSON.parse(event.data); // 替换为函数式更新 setListState(prevList => [...prevList, data]); };
方案2:用useRef存储最新state(适用于回调内需要访问多个最新state的场景)
如果回调内不止需要用到listState,还要同时读取其他多个state的最新值,可以用ref存储state的最新引用,避免闭包捕获旧值:
function App(){ const [listState, setListState] = useState([]); // 新建ref同步存储最新的listState const listRef = useRef(listState); // listState更新时同步更新ref的值 useEffect(() => { listRef.current = listState; }, [listState]); let connectWebsocket = () => { let ws = new WebSocket(`ws://${window.location.host}/ws`); ws.onmessage = (event) => { let data = JSON.parse(event.data); // 从ref中读取最新值更新 setListState([...listRef.current, data]); }; } // 其余原有逻辑保持不变... }
额外优化建议
记得在组件卸载时主动关闭websocket连接,避免内存泄漏,调整对应逻辑如下:
useEffect(() => { const ws = connectWebsocket(); // 组件卸载时关闭长连接 return () => { ws.close(); } }, []); // 同步修改connectWebsocket返回ws实例 let connectWebsocket = () => { let ws = new WebSocket(`ws://${window.location.host}/ws`); ws.onmessage = (event) => { let data = JSON.parse(event.data); setListState(prevList => [...prevList, data]); }; return ws; }
内容的提问来源于stack exchange,提问作者ahliru
相关产品推荐
相关产品推荐

