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

Socket.io+React+Node聊天应用重复发送消息问题求解

Socket.io重复发送消息问题解决

我用React和Node.js搭建了一个测试用聊天应用,用来学习Socket.io的工作机制。目前其他功能都正常,但存在一个问题:点击提交按钮发送消息时,消息的发送次数是输入字符数的两倍。请问该怎么解决?

客户端代码

import React from "react";
import { useState } from "react";
import { io } from "socket.io-client";

const App = () => {

  const socket = io("http://localhost:5000");
  const [input, setInput] = useState("");

  const handleChange = (e) => {
    setInput(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    socket.emit("chat-message", input);
  };

  socket.on("chat-message", (msg) => {
    console.log(msg);
  });

  return (
    <div className="App">
      <h1>Chat</h1>
      <form onSubmit={handleSubmit}>
        <input onChange={handleChange} type="text" id="input" />
        <button type="submit">Send</button>
      </form>
      <div id="messages">

      </div>
    </div>
  );
};
export default App;

服务端代码

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);

const io = require('socket.io')(server, {
    cors: {
        origin: '*',
    }
});

io.on('connection', (socket) => {
    socket.on('chat-message', (msg) => {
        io.emit('chat-message', msg);
    })
});

server.listen(5000, () => {
  console.log('listening on: 5000');
});

问题原因

核心问题出在React组件的渲染逻辑上:你的函数组件每次渲染(比如输入框内容变化触发setInput时),都会重新执行组件内的所有代码,包括创建新的Socket实例,并且重复绑定chat-message事件监听器。这就导致:

  • 每输入一个字符,就会新建一个Socket连接
  • 同一个事件被绑定多次,收到消息时回调会触发多次

修复方案

1. 确保Socket实例唯一且事件监听只绑定一次

用React的useRef保存Socket实例,配合useEffect在组件挂载时初始化连接、绑定事件,卸载时清理资源:

import React, { useState, useEffect, useRef } from "react";
import { io } from "socket.io-client";

const App = () => {
  const [input, setInput] = useState("");
  // 用useRef存储Socket实例,避免每次渲染重新创建
  const socketRef = useRef(null);

  useEffect(() => {
    // 组件挂载时创建Socket连接
    socketRef.current = io("http://localhost:5000");

    // 定义事件回调函数,方便后续移除监听
    const handleChatMessage = (msg) => {
      console.log(msg);
    };
    // 绑定事件
    socketRef.current.on("chat-message", handleChatMessage);

    // 组件卸载时清理:移除监听、断开连接
    return () => {
      socketRef.current.off("chat-message", handleChatMessage);
      socketRef.current.disconnect();
    };
  }, []); // 空依赖数组,确保只执行一次

  const handleChange = (e) => {
    setInput(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // 使用ref中的Socket实例发送消息
    socketRef.current.emit("chat-message", input);
    // 可选:发送后清空输入框
    setInput("");
  };

  return (
    <div className="App">
      <h1>Chat</h1>
      <form onSubmit={handleSubmit}>
        {/* 给input加上value绑定,确保受控组件 */}
        <input onChange={handleChange} type="text" id="input" value={input} />
        <button type="submit">Send</button>
      </form>
      <div id="messages"></div>
    </div>
  );
};
export default App;

2. 服务端代码无需修改

你的服务端逻辑是正常的:收到客户端消息后广播给所有连接的客户端,不需要做调整。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:45:32