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

基于MERN+Socket.io的聊天应用两处技术问题求助

MERN + Socket.io 聊天应用问题解决

问题1:socket.emit传递函数而非消息值

你当前的sendMessage函数里,socket.emit('send',(data)=>{setList(...)})把回调函数当成了要发送的消息数据传给服务端,完全不符合Socket.io的emit用法。正确逻辑是第一个参数为事件名,第二个是发送的消息数据,第三个才是可选的回调函数。

修复代码:

客户端sendMessage函数:

const sendMessage=(e)=>{
    e.preventDefault(); 
    const msg = message.trim();
    if (!msg) return; // 过滤空消息
    // 正确传递消息数据,回调用于服务端确认消息接收
    socket.emit('send', msg, () => {
        setMessage(''); // 发送后清空输入框
    });
    // 本地直接添加消息,避免等待服务端广播产生延迟
    setList(prev => [...prev, msg]);
}

服务端(可选,支持回调确认):

socket.on("send", (data, callback) => {
    socket.broadcast.emit("rec", data); // 只发给其他客户端,避免发送者重复收到
    console.log(data);
    // 调用客户端回调,确认消息已接收
    if (callback) callback();
});

问题2:消息被两次添加到列表

重复添加的核心原因有两个:

  1. 组件每次渲染都会创建新的Socket.io客户端实例,导致多个连接同时监听rec事件,收到消息时多次触发列表更新;
  2. 服务端用io.emit会把消息发给所有客户端(包括发送者自己),如果发送者本地已经添加了消息,就会造成重复。

修复代码:

客户端:

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

function Room(props) {
    const [message,setMessage]=useState('');
    const [list, setList] = useState([]);
    const ENDPOINT = "http://localhost:5000";
    // 用useMemo缓存socket实例,仅在组件挂载时创建一次
    const socket = useMemo(() => socketIOClient(ENDPOINT, {
        credentials: true
    }), []);

    const sendMessage=(e)=>{
        e.preventDefault(); 
        const msg = message.trim();
        if (!msg) return;
        socket.emit('send', msg, () => {
            setMessage('');
        });
        setList(prev => [...prev, msg]);
    }

    useEffect(()=>{
        // 抽离监听函数,方便组件卸载时移除监听
        const handleReceive = (data) => {
            setList(prev => [...prev, data]);
        };
        socket.on('rec', handleReceive);
        // 组件卸载时清除监听,防止内存泄漏
        return () => {
            socket.off('rec', handleReceive);
        };
    }, [socket]);

    // 修复列表渲染判断:用length判断而非引用比较
    return (
        <div>
            <div>
                {list.length > 0 && list.map((el,i)=>{
                    return <p key={i}>{el}</p>
                })}
            </div>
            <div>
                <form>
                    <input value={message} onChange={e=>setMessage(e.target.value)} />
                    <button onClick={sendMessage}>Send</button>
                </form>
            </div>
        </div>
    )
}

export default Room;

服务端:

将io.emit改为socket.broadcast.emit,仅把消息广播给除发送者之外的其他客户端:

io.on("connection", (socket) => {
    socket.emit('greeting', {
        greeting: 'Hello Client'
    })
  
    socket.on("send", (data, callback) => {
        socket.broadcast.emit("rec", data); // 仅广播给其他客户端
        console.log(data);
        if (callback) callback();
      });
  });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 16:54:53