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
相关产品推荐
相关产品推荐

