如何在Node.js中通过Socket实现React群聊的‘用户正在输入’提示
实现群聊中“用户正在输入”的Socket提示功能
后端(Node.js + Socket.io)
先在Node.js服务端配置Socket.io,处理客户端的输入状态事件,再广播给同房间的其他用户:
- 初始化Socket.io服务
const express = require('express'); const http = require('http'); const { Server } = require('socket.io'); const app = express(); const server = http.createServer(app); const io = new Server(server, { cors: { origin: "http://localhost:3000" // 替换为你的React前端地址 } }); // 存储每个用户的输入超时定时器,避免重复触发 const typingTimeouts = new Map(); io.on('connection', (socket) => { // 用户加入指定群聊房间 socket.on('join-room', (roomId, username) => { socket.join(roomId); socket.roomId = roomId; socket.username = username; }); // 处理用户正在输入事件 socket.on('user-typing', () => { const { roomId, username } = socket; // 清除之前的超时定时器,防止重复发送停止输入事件 if (typingTimeouts.has(username)) { clearTimeout(typingTimeouts.get(username)); typingTimeouts.delete(username); } // 广播给同房间其他用户,告知当前用户正在输入 socket.to(roomId).emit('user-typing', username); // 设置3秒超时,无新输入则自动触发停止输入 const timeout = setTimeout(() => { socket.to(roomId).emit('stop-typing', username); typingTimeouts.delete(username); }, 3000); typingTimeouts.set(username, timeout); }); // 手动触发停止输入(比如用户点击输入框外) socket.on('stop-typing', () => { const { roomId, username } = socket; if (typingTimeouts.has(username)) { clearTimeout(typingTimeouts.get(username)); typingTimeouts.delete(username); } socket.to(roomId).emit('stop-typing', username); }); // 用户断开连接时清理状态 socket.on('disconnect', () => { const { username } = socket; if (typingTimeouts.has(username)) { clearTimeout(typingTimeouts.get(username)); typingTimeouts.delete(username); } if (socket.roomId) { socket.to(socket.roomId).emit('stop-typing', username); } }); }); server.listen(3001, () => { console.log('Server running on port 3001'); });
前端(React)
在React组件中连接Socket.io,监听输入框状态、发送输入事件,并渲染正在输入的提示:
- 安装Socket.io客户端
npm install socket.io-client
- React组件实现
import { useState, useEffect, useRef } from 'react'; import { io } from 'socket.io-client'; const ChatRoom = ({ roomId, username }) => { const [message, setMessage] = useState(''); const [typingUsers, setTypingUsers] = useState(new Set()); const socketRef = useRef(null); const typingTimeoutRef = useRef(null); useEffect(() => { // 初始化Socket连接 socketRef.current = io('http://localhost:3001'); // 替换为你的后端地址 socketRef.current.emit('join-room', roomId, username); // 监听其他用户正在输入的事件 socketRef.current.on('user-typing', (user) => { setTypingUsers(prev => new Set(prev).add(user)); }); // 监听其他用户停止输入的事件 socketRef.current.on('stop-typing', (user) => { setTypingUsers(prev => { const updated = new Set(prev); updated.delete(user); return updated; }); }); return () => { socketRef.current.disconnect(); }; }, [roomId, username]); // 处理输入框变化,发送正在输入事件(防抖处理) const handleInputChange = (e) => { setMessage(e.target.value); if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current); } // 输入停止500ms后再发送事件,避免频繁触发 typingTimeoutRef.current = setTimeout(() => { socketRef.current.emit('user-typing'); }, 500); }; // 输入框失焦时发送停止输入事件 const handleInputBlur = () => { socketRef.current.emit('stop-typing'); if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current); } }; // 渲染正在输入的提示文本 const renderTypingHint = () => { const usersArray = Array.from(typingUsers); if (usersArray.length === 0) return null; if (usersArray.length === 1) { return <p className="typing-hint">{usersArray[0]}正在输入...</p>; } else { return <p className="typing-hint">{usersArray.join('、')}正在输入...</p>; } }; return ( <div className="chat-room"> <div className="chat-messages"> {/* 聊天消息列表渲染区域 */} </div> {renderTypingHint()} <input type="text" value={message} onChange={handleInputChange} onBlur={handleInputBlur} placeholder="输入消息..." /> <button onClick={() => { // 发送消息逻辑 socketRef.current.emit('send-message', roomId, username, message); setMessage(''); // 发送消息后触发停止输入 socketRef.current.emit('stop-typing'); }}>发送</button> </div> ); }; export default ChatRoom;
关键细节说明
- 防抖/超时逻辑:前后端都加入了防抖处理,避免频繁发送Socket事件,减少服务器压力和前端UI频繁更新。
- 房间隔离:通过
join-room事件让用户加入指定房间,确保“正在输入”提示只在当前群聊内显示。 - 状态清理:用户断开连接、输入停止或发送消息时,及时清理定时器并广播停止输入事件,避免提示残留。
内容的提问来源于stack exchange,提问作者user15779851
相关产品推荐
相关产品推荐

