React与Node.js中Socket.IO无法正常接收数据问题求助
问题:Socket.IO前后端连接无法接收第二个JSON数据
我使用Socket.IO搭建Express/Node.js后端与React前端的连接,需求是点击按钮时接收后端发送的JSON数据。当前点击第一个按钮可收到含"hello"的JSON消息,但点击“Done with JSON”按钮后,无法收到第二个JSON消息,页面仅显示“Done with JSON”。
前后端代码
后端代码
//////server side const express = require('express'); const http = require('http'); const { Server } = require('socket.io'); const app = express(); const server = app.listen(8080); const io = require('socket.io')(server, { cors: { origin: '*', } }); PORT = 8080; app.get('/', (req, res) => { res.send('Socket.IO Server Running'); }); io.on('connection', (socket)=>{ console.log("Connected"); // 客户端能收到这条消息 const firstJson = {message:"hello"}; socket.emit("receiveJson", firstJson); socket.on('jsonProcessed', () => { console.log('Client has processed the first JSON.'); setTimeout(() => { // 客户端收不到这条消息 const secondJson = { message: 'This is your second JSON!' }; socket.emit('receiveJson', secondJson); }, 1000); // 1秒延迟 }); socket.on('disconnect', ()=>{ console.log('User disconnected'); }); });
前端代码
//Client side import React, {useRef, useState, useEffect} from 'react'; import { useNavigate } from 'react-router-dom'; import io from 'socket.io-client'; const Male = ({title})=>{ const navigate = useNavigate(); const socket = io('http://localhost:8080'); const [jsonData, setJsonData] = useState(null); useEffect(()=>{ socket.on('connect',()=>{ console.log('Connection to server established'); }) socket.on('receiveJson',(data)=>{ console.log('Received JSON from server: ', data); setJsonData(data); }) return()=>{ console.log("Disconnected from socket"); socket.disconnect(); } },[]); const handleJsonProcessing = () =>{ console.log('Emitting jsonProcessed event'); socket.emit('jsonProcessed'); setJsonData(null); }; return( <div> {jsonData ? ( <div> <pre>{JSON.stringify(jsonData, null, 2)}</pre> <button onClick={handleJsonProcessing}>Done with JSON</button> </div> ) : ( <p>No JSON data received.!!</p> )} </div> ); };
问题原因
核心问题在于Socket.IO实例的创建位置错误:在函数组件顶层直接创建socket实例,每次组件重新渲染(比如调用setJsonData(null)时)都会生成新的Socket.IO连接,旧连接被断开。而useEffect仅在组件挂载时执行一次,只给第一个socket实例绑定了receiveJson事件,后续新创建的socket没有绑定该事件,导致无法接收后端发送的第二个JSON。
解决方案
使用useRef保存Socket.IO实例,避免每次渲染重新创建连接,并确保事件监听绑定到正确的实例上:
修改后的前端代码
import React, {useRef, useState, useEffect} from 'react'; import { useNavigate } from 'react-router-dom'; import io from 'socket.io-client'; const Male = ({title})=>{ const navigate = useNavigate(); // 用useRef保存socket实例,避免重复创建 const socketRef = useRef(null); const [jsonData, setJsonData] = useState(null); useEffect(()=>{ // 在useEffect内初始化socket,确保只创建一次 const socket = io('http://localhost:8080'); socketRef.current = socket; socket.on('connect',()=>{ console.log('Connection to server established'); }); socket.on('receiveJson',(data)=>{ console.log('Received JSON from server: ', data); setJsonData(data); }); return()=>{ console.log("Disconnected from socket"); socket.disconnect(); }; },[]); const handleJsonProcessing = () =>{ console.log('Emitting jsonProcessed event'); // 使用ref保存的socket实例发送事件 socketRef.current.emit('jsonProcessed'); setJsonData(null); }; return( <div> {jsonData ? ( <div> <pre>{JSON.stringify(jsonData, null, 2)}</pre> <button onClick={handleJsonProcessing}>Done with JSON</button> </div> ) : ( <p>No JSON data received.!!</p> )} </div> ); };
说明
useRef创建的对象在组件整个生命周期内保持引用不变,不会随渲染重新创建,确保始终使用同一个Socket.IO连接。- 事件监听在
useEffect内绑定到初始化的socket实例,卸载时断开连接,避免内存泄漏。 - 发送事件时调用
socketRef.current.emit,保证使用的是同一个连接实例,后端能正确响应并发送第二个JSON。
内容的提问来源于stack exchange,提问作者Syed S
相关产品推荐
相关产品推荐

