React中如何通过REST预加载数据并结合WebSocket实时更新列表
问题根因
- 原代码中WebSocket实例创建逻辑写在组件顶层,每次组件重新渲染都会执行这段代码,导致重复新建连接
- onmessage回调形成闭包,捕获的是首次渲染时的初始
objects值(空数组),每次触发回调都会基于空数组修改,导致旧数据丢失
正确实现代码
import { useState, useEffect } from 'react'; import axios from 'axios'; const ObjectList = () => { const [objects, setObjects] = useState([]); useEffect(() => { // 执行REST预加载初始数据 const fetchInitialData = async () => { try { const resp = await axios.get(`${process.env.REACT_APP_REST_API}/objects`); setObjects(resp.data); } catch (err) { // 可自行补充错误处理逻辑 console.error('初始数据加载失败', err); } }; fetchInitialData(); // 初始化WebSocket,仅执行一次 const ws = new WebSocket(`${process.env.REACT_APP_WS_API}/objects`); ws.onmessage = (event) => { const obj = JSON.parse(event.data); // 使用setState函数式更新,直接获取最新的state值,规避闭包旧值问题 setObjects(prevObjects => { const index = prevObjects.findIndex(it => it.id === obj.id); if (index === -1) { return [...prevObjects, obj]; } else { const newObjects = [...prevObjects]; newObjects[index] = obj; return newObjects; } }); }; // 组件卸载时关闭WebSocket连接,避免内存泄漏 return () => { ws.close(); }; }, []); // 空依赖数组保证整个逻辑仅在组件首次挂载时执行一次 // 列表渲染逻辑可自行扩展 return ( <ul> {objects.map(obj => <li key={obj.id}>{obj.name}</li>)} </ul> ); };
核心优化点
- 将WebSocket初始化逻辑放入依赖为空的
useEffect中,配合卸载清理逻辑,保证组件生命周期内仅建立一次连接 - 状态更新采用函数式更新的写法,直接从参数中拿到最新的列表值,完全规避闭包捕获旧状态的问题
- 初始加载和WebSocket初始化放在同一个
useEffect中统一管理,时序更可控;如果需要严格保证初始数据加载完成再处理WS事件,可新增一个加载完成的状态标记,在onmessage中判断标记后再执行更新即可
内容的提问来源于stack exchange,提问作者Sasha Shpota
相关产品推荐
相关产品推荐

