Socket Context Provider在React组件中无法正常使用的问题排查
解决Socket.io客户端组件无法监听userCount事件的问题
问题原因分析
- 服务器端发送时机不匹配:若服务器仅在客户端首次连接时推送
userCount,而组件的监听逻辑在socket连接完成后才执行(组件挂载晚于连接建立),会错过初始消息。 - Next.js SSR环境的实例异常:模块顶层直接创建socket实例会在服务器端执行,导致客户端复用的实例存在兼容性问题,或组件初始化时socket未完成客户端侧的连接。
- 缺少监听清理逻辑:虽非直接诱因,但会引发后续内存泄漏或重复监听问题。
具体解决方案
1. 修改Socket上下文,适配Next.js SSR环境
将socket实例的创建移至客户端useEffect中,避免服务器端生成无效实例:
// context/socket.tsx import React, { createContext, useEffect, useState } from "react"; import { io, Socket } from "socket.io-client"; const SocketContext = createContext<Socket | null>(null); const SocketProvider = ({ children }: { children: React.ReactNode }) => { const [socket, setSocket] = useState<Socket | null>(null); useEffect(() => { // 仅在客户端环境创建socket连接 if (typeof window === 'undefined') return; const newSocket = io("http://localhost:3000", { reconnectionDelay: 1000, reconnection: true, reconnectionAttempts: 10, agent: false, upgrade: false, rejectUnauthorized: false, }); newSocket.on("connect", () => { console.log("someone connected: ", newSocket?.id); }); setSocket(newSocket); // 组件卸载时断开连接 return () => { newSocket.disconnect(); }; }, []); return ( <SocketContext.Provider value={socket}>{children}</SocketContext.Provider> ); }; export { SocketContext, SocketProvider };
2. 调整服务器端发送逻辑
确保服务器能响应客户端主动请求,并在客户端连接/断开时广播在线人数:
// 服务器端Node.js示例代码 const { Server } = require("socket.io"); const io = new Server(3000, { cors: { origin: "http://localhost:3000" } // 按需修改前端地址 }); io.on("connection", (socket) => { // 响应客户端主动请求,推送当前在线数 socket.on("requestUserCount", () => { socket.emit("userCount", io.engine.clientsCount); }); // 新客户端连接时,广播更新所有客户端的在线数 io.emit("userCount", io.engine.clientsCount); // 客户端断开时,广播更新在线数 socket.on("disconnect", () => { io.emit("userCount", io.engine.clientsCount); }); });
3. 优化Home组件的监听逻辑
添加主动请求逻辑确保组件挂载后获取最新数据,并清理监听避免内存泄漏:
// components/Home.tsx import { SocketContext } from '../context/socket'; import { useAccount } from 'wagmi'; import { useState, useEffect, useContext } from 'react'; import type { NextPage } from 'next'; const Home: NextPage = () => { const { address } = useAccount(); const [userCount, setUserCount] = useState(0); const socket = useContext(SocketContext); useEffect(() => { if (!socket) return; // 定义独立处理函数,方便后续移除监听 const handleUserCount = (data: number) => { console.log(data); setUserCount(data); }; // 主动向服务器请求当前在线数 socket.emit('requestUserCount'); // 监听userCount事件 socket.on('userCount', handleUserCount); // 组件卸载时移除监听 return () => { socket.off('userCount', handleUserCount); }; }, [socket]); // 依赖socket实例,确保就绪后执行逻辑 return ( <div> <p>当前在线人数:{userCount}</p> {/* 其他组件内容 */} </div> ); }; export default Home;
内容的提问来源于stack exchange,提问作者kalculated
相关产品推荐
相关产品推荐

