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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 08:44:55