You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React集成Chakra UI时useEffect未触发Socket.io事件的问题

问题

用create-react-app加Chakra UI做入门开发,目标是搭一个基于react-plotly的实时数据查看器。Socket.io服务器收到send_plot_data事件后,每秒会推送新的JSON数据,UI用plotly-react绘图。不带Chakra UI的代码完全正常,但加了之后,useEffect里请求配置的get_config和请求绘图数据的send_plot_data这两个socket.emit操作没触发(已经查过Socket.io服务器日志确认)。除了按钮相关代码,其他基本没改,按钮本来还要触发事件,根据接收的JSON数据填充下拉框选项。

原代码

import React, { useEffect, useState } from 'react';
import {io} from 'socket.io-client';
import Plot from 'react-plotly.js';

import {
  ChakraProvider, Flex, Box, Spinner, Container, Text
} from '@chakra-ui/react';

const socket = io("http://192.168.0.101:5000");

const App = () => {
  const [data, setData] = useState();
  const [loading, setLoading] = useState(true);

  useEffect(() => {
      socket.on("connect",()=>{
        console.log(`Connected`);
        socket.emit("send_plot_data", {symbol: "default"});
        socket.emit("get_config", {config: ""});
      });
      socket.on("do_graph", res => {
        console.log(`Graph plot ok`)
        if(loading===true){
          setLoading(false);
        }
        let response = JSON.parse(res);
        response.config = {responsive: true}
        setData(response);
      });
      socket.on("connect_error", (err) => {
        console.log(`connect_error due to ${err.message}`);
      });
      return () => socket.disconnect();

  //eslint-disable-next-line
  }, []);

  return (
   <ChakraProvider resetCSS>
         <Box>
             {loading?(
                <Flex flexDirection="column" alignItems="center">
                  <Container minHeight="10vh">
                  </Container>
                  <Spinner />
                  <Text fontSize='lg'> Loading plot data ... </Text>
                </Flex>
              ):(
                <Plot {...data} />
              )}
          </Box>
    </ChakraProvider>
  );
}
原因分析与修复方案

核心原因

Socket实例在组件外部提前初始化了,ChakraProvider会改变React组件树的初始化时机,导致socket的connect事件在组件的useEffect注册监听之前就已经触发了,后续useEffect里的connect事件监听根本捕获不到已经完成的连接,所以不会执行那两个emit操作。

修复代码

把Socket实例的初始化移到组件内部的useEffect里,确保连接事件监听在socket创建后立刻注册,不会错过连接时机:

import React, { useEffect, useState } from 'react';
import {io} from 'socket.io-client';
import Plot from 'react-plotly.js';

import {
  ChakraProvider, Flex, Box, Spinner, Container, Text
} from '@chakra-ui/react';

const App = () => {
  const [data, setData] = useState();
  const [loading, setLoading] = useState(true);
  // 不再在外部初始化socket
  let socket;

  useEffect(() => {
      // 在useEffect内部创建socket实例
      socket = io("http://192.168.0.101:5000");
      
      socket.on("connect",()=>{
        console.log(`Connected`);
        socket.emit("send_plot_data", {symbol: "default"});
        socket.emit("get_config", {config: ""});
      });
      socket.on("do_graph", res => {
        console.log(`Graph plot ok`)
        if(loading===true){
          setLoading(false);
        }
        let response = JSON.parse(res);
        response.config = {responsive: true}
        setData(response);
      });
      socket.on("connect_error", (err) => {
        console.log(`connect_error due to ${err.message}`);
      });
      return () => socket.disconnect();

  }, []);

  return (
   <ChakraProvider resetCSS>
         <Box>
             {loading?(
                <Flex flexDirection="column" alignItems="center">
                  <Container minHeight="10vh">
                  </Container>
                  <Spinner />
                  <Text fontSize='lg'> Loading plot data ... </Text>
                </Flex>
              ):(
                <Plot {...data} />
              )}
          </Box>
    </ChakraProvider>
  );
}

额外说明

如果后续需要在组件其他地方(比如按钮点击事件)使用socket,记得确保socket实例已经初始化,也可以把socket存在useRef里避免重复创建:

// 用useRef保存socket实例
const socketRef = useRef(null);

useEffect(() => {
  socketRef.current = io("http://192.168.0.101:5000");
  // 后续用socketRef.current调用emit等方法
  // ...
}, []);

内容的提问来源于stack exchange,提问作者HermDP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 11:10:40