React+Socket.IO单PC连接网站出现多服务器连接问题求助
React + Socket.IO 多连接问题修复及实时同步实现
我需要实现的功能是:用户在前端提交数据后,其他打开页面的用户无需手动刷新,就能实时同步看到新数据。但开发过程中遇到一个问题:仅用一台电脑访问网站,服务器却显示多个连接,尝试删除旧连接也无法解决。
以下是当前的前端App.js和后端index.js代码:
前端App.js代码
import React, { useEffect, useState } from "react"; import Header from "./Header"; import Footer from "./Footer"; import Note from "./Note"; import CreateArea from "./CreateArea"; import Axios from "axios"; import io from "socket.io-client"; function App() { const [notes, setNotes] = useState([]); const [noteForDeletion, setNoteForDeletion] = useState(null); const [loading, setLoading] = useState(false); const socket = io.connect("http://192.168.1.158:3001"); async function fetchNotes() { const dbnotes = await Axios.get("http://192.168.1.158:1337/api/notes"); const newNotes = []; dbnotes.data.data.forEach((note) => { newNotes.push({ title: note.attributes.title, content: note.attributes.content, id: note.id, }); }); setNotes(newNotes); } useEffect(() => { if (noteForDeletion !== null) return; fetchNotes(); socket.on("receive_note", (data) => { alert(data.data); }); }, [socket]); async function addNotesFromAPI(newNote) { setLoading(true); const data = { data: { title: newNote.title, content: newNote.content, }, }; try { const response = await Axios.post( "http://192.168.1.158:1337/api/notes", data ); const newNoteData = { title: response.data.data.attributes.title, content: response.data.data.attributes.content, id: response.data.data.id, }; socket.emit("add_note", data); setNotes((prevNotes) => { return [...prevNotes, newNoteData]; }); } catch (error) { console.log("Error adding note: ", error); } setLoading(false); } async function deleteNote(id) { setLoading(true); setNoteForDeletion(id); try { await Axios.delete(`http://192.168.1.158:1337/api/notes/${id}`); setNotes((prevNotes) => { return prevNotes.filter((noteItem) => noteItem.id !== id); }); setNoteForDeletion(null); } catch (error) { console.log("Error found: ", error); } setLoading(false); } return ( <div> <Header /> <CreateArea onAdd={addNotesFromAPI} /> {loading ? ( <div className="loading-container"> <div className="loading-text">Loading...</div> </div> ) : ( notes.map((noteItem) => ( <Note key={noteItem.id} id={noteItem.id} title={noteItem.title} content={noteItem.content} onDelete={() => deleteNote(noteItem.id)} /> )) )} <Footer /> </div> ); } export default App;
后端index.js代码
const express = require("express"); const app = express(); const http = require("http"); const { Server } = require("socket.io"); const cors = require("cors"); app.use(cors()); const server = http.createServer(app); const connectedUsers = new Set(); const io = new Server(server, { cors: { origin: "http://192.168.1.158:3000", methods: ["GET", "POST", "DELETE"], }, }); io.on("connection", (socket) => { console.log(`User Connected: ${socket.id}`); connectedUsers.add(socket.id); socket.on("add_note", (data) => { socket.broadcast.emit("receive_note", data); }); socket.on("disconnect", () => { console.log(`User Disconnected: ${socket.id}`); connectedUsers.delete(socket.id); }); }); server.listen(3001, () => { console.log("Server is up!"); });
问题根源
核心问题是Socket连接被重复创建。当前代码中io.connect()直接写在App组件顶层,每次组件重新渲染(比如notes状态更新、loading状态变化),都会生成一个新的Socket连接,导致服务器端收到多个连接请求。
解决方案
1. 持久化Socket连接实例
使用React的useRef存储Socket实例,确保组件生命周期内只创建一次连接;同时用useEffect处理连接的初始化和清理逻辑。
2. 修改后的前端App.js代码
import React, { useEffect, useState, useRef } from "react"; import Header from "./Header"; import Footer from "./Footer"; import Note from "./Note"; import CreateArea from "./CreateArea"; import Axios from "axios"; import io from "socket.io-client"; function App() { const [notes, setNotes] = useState([]); const [noteForDeletion, setNoteForDeletion] = useState(null); const [loading, setLoading] = useState(false); // 用useRef持久化Socket实例,避免重复创建 const socketRef = useRef(null); async function fetchNotes() { const dbnotes = await Axios.get("http://192.168.1.158:1337/api/notes"); const newNotes = []; dbnotes.data.data.forEach((note) => { newNotes.push({ title: note.attributes.title, content: note.attributes.content, id: note.id, }); }); setNotes(newNotes); } useEffect(() => { // 仅初始化一次Socket连接 socketRef.current = io.connect("http://192.168.1.158:3001"); // 监听新增笔记事件,实时更新本地列表 const handleReceiveNote = (data) => { setNotes(prevNotes => [...prevNotes, data]); }; socketRef.current.on("receive_note", handleReceiveNote); // 监听删除笔记事件,实时更新本地列表 const handleReceiveDelete = (noteId) => { setNotes(prevNotes => prevNotes.filter(note => note.id !== noteId)); }; socketRef.current.on("receive_delete", handleReceiveDelete); // 初始加载数据 if (noteForDeletion === null) { fetchNotes(); } // 组件卸载时清理连接和事件监听 return () => { socketRef.current.off("receive_note", handleReceiveNote); socketRef.current.off("receive_delete", handleReceiveDelete); socketRef.current.disconnect(); }; }, [noteForDeletion]); async function addNotesFromAPI(newNote) { setLoading(true); const data = { data: { title: newNote.title, content: newNote.content, }, }; try { const response = await Axios.post( "http://192.168.1.158:1337/api/notes", data ); const newNoteData = { title: response.data.data.attributes.title, content: response.data.data.attributes.content, id: response.data.data.id, }; // 发送新增事件给其他用户 socketRef.current.emit("add_note", newNoteData); setNotes((prevNotes) => { return [...prevNotes, newNoteData]; }); } catch (error) { console.log("Error adding note: ", error); } setLoading(false); } async function deleteNote(id) { setLoading(true); setNoteForDeletion(id); try { await Axios.delete(`http://192.168.1.158:1337/api/notes/${id}`); setNotes((prevNotes) => { return prevNotes.filter((noteItem) => noteItem.id !== id); }); // 发送删除事件给其他用户 socketRef.current.emit("delete_note", id); setNoteForDeletion(null); } catch (error) { console.log("Error found: ", error); } setLoading(false); } return ( <div> <Header /> <CreateArea onAdd={addNotesFromAPI} /> {loading ? ( <div className="loading-container"> <div className="loading-text">Loading...</div> </div> ) : ( notes.map((noteItem) => ( <Note key={noteItem.id} id={noteItem.id} title={noteItem.title} content={noteItem.content} onDelete={() => deleteNote(noteItem.id)} /> )) )} <Footer /> </div> ); } export default App;
3. 修改后的后端index.js代码
const express = require("express"); const app = express(); const http = require("http"); const { Server } = require("socket.io"); const cors = require("cors"); app.use(cors()); const server = http.createServer(app); const connectedUsers = new Set(); const io = new Server(server, { cors: { origin: "http://192.168.1.158:3000", methods: ["GET", "POST", "DELETE"], }, }); io.on("connection", (socket) => { console.log(`User Connected: ${socket.id}`); connectedUsers.add(socket.id); socket.on("add_note", (data) => { socket.broadcast.emit("receive_note", data); }); // 新增删除事件转发逻辑 socket.on("delete_note", (noteId) => { socket.broadcast.emit("receive_delete", noteId); }); socket.on("disconnect", () => { console.log(`User Disconnected: ${socket.id}`); connectedUsers.delete(socket.id); }); }); server.listen(3001, () => { console.log("Server is up!"); });
内容的提问来源于stack exchange,提问作者Vard
相关产品推荐
相关产品推荐

