React中useState结合WebSocket更新折线图数据失败求助
React函数组件中WebSocket数据无法更新折线图的问题修复
问题根源
- 直接修改全局变量:你之前直接修改导出的
mockLineData全局变量,React不会追踪这种外部修改,自然不会触发组件重渲染。 - x值生成逻辑完全错误:用
setInterval的返回值(定时器ID)作为x轴数据,这根本不是你需要的递增数值,还会创建一堆无用的定时器。 - 组件状态更新无效:LineChart里导入的
data是初始静态值,就算全局变量变了,这个导入值也不会自动更新,setInterval里反复setState(data)相当于重复设置同一个初始值,完全起不到更新作用。
修复后的完整代码
import { useState, useEffect, useRef } from "react"; import { ResponsiveLine } from "@nivo/line"; import { useTheme } from "@mui/material/styles"; import tokens from "../theme/tokens"; // 按你的实际路径调整 const LineChart = ({ isCustomLineColors = false, isDashboard = false }) => { const theme = useTheme(); const colors = tokens(theme.palette.mode); // 用组件内部状态维护折线图数据,初始值和之前的mockLineData一致 const [chartData, setChartData] = useState([ { id: "ИБП Uвх", color: tokens("dark").redAccent[200], data: [{ x: 0, y: 0 }], }, ]); // 用ref追踪x轴递增数值,避免闭包导致的数值不更新 const xCounterRef = useRef(1); // 用ref保存WebSocket实例,防止组件重渲染时重复创建连接 const wsRef = useRef(null); useEffect(() => { // 初始化WebSocket连接 const ws = new WebSocket("ws://localhost:8000"); wsRef.current = ws; ws.onopen = () => { console.log("подключился"); }; ws.onmessage = (message) => { try { const jsonParse = JSON.parse(message.data); const newValue = jsonParse[0]["value_state"]; // 用函数式更新状态,确保基于最新数据修改 setChartData((prevData) => { const updatedSeries = [...prevData]; let updatedPoints = [...updatedSeries[0].data]; // 保持数据点数量不超过50个 if (updatedPoints.length >= 50) { updatedPoints.shift(); } // 添加新数据点,x值自增 updatedPoints.push({ x: xCounterRef.current, y: newValue }); xCounterRef.current += 1; updatedSeries[0].data = updatedPoints; return updatedSeries; }); } catch (error) { console.log("Error: ", error); } }; // 组件卸载时关闭WebSocket,防止内存泄漏 return () => { ws.close(); }; }, []); return ( <ResponsiveLine data={chartData} theme={{ axis: { domain: { line: { stroke: colors.grey[100], }, }, legend: { text: { fill: colors.grey[100], }, }, ticks: { line: { stroke: colors.grey[100], strokeWidth: 1, }, text: { fill: colors.grey[100], }, }, }, legends: { text: { fill: colors.grey[100], }, }, tooltip: { container: { color: colors.primary[500], }, }, }} colors={isDashboard ? { datum: "color" } : { scheme: "nivo" }} margin={{ top: 50, right: 110, bottom: 50, left: 60 }} enableGridX={false} enableGridY={false} pointSize={8} pointColor={{ theme: "background" }} pointBorderWidth={2} pointBorderColor={{ from: "serieColor" }} pointLabelYOffset={-12} useMesh={true} legends={[ { anchor: "bottom-right", direction: "column", justify: false, translateX: 100, translateY: 0, itemsSpacing: 0, itemDirection: "left-to-right", itemWidth: 80, itemHeight: 20, itemOpacity: 0.75, symbolSize: 12, symbolShape: "circle", symbolBorderColor: "rgba(0, 0, 0, .5)", effects: [ { on: "hover", style: { itemBackground: "rgba(0, 0, 0, .03)", itemOpacity: 1, }, }, ], }, ]} /> ); }; export default LineChart;
关键改动说明
- 放弃全局变量,用组件内部状态管理:所有数据更新都通过
setChartData触发,React会自动检测状态变化并重新渲染折线图。 - WebSocket实例用ref保存:避免组件每次重渲染都新建连接,同时在组件卸载时自动关闭连接,防止内存泄漏。
- 用ref追踪x值:解决了闭包导致的数值不更新问题,确保x值能持续递增。
- 函数式更新状态:每次更新都基于最新的状态数据修改,避免状态更新滞后的问题。
内容的提问来源于stack exchange,提问作者Michael Shiko
相关产品推荐
相关产品推荐

