Zen Eddin Allaham نشر 6 أغسطس أرسل تقرير نشر 6 أغسطس السلام عليكم، أواجه مشكلة في نظام Realtime Notifications باستخدام NestJS + Socket.IO + React (Next.js) ولم أتمكن من معرفة سببها. وصف المشكلة اتصال الـ Socket يتم بنجاح. المستخدم يُضاف إلى قائمة onlineUsers. عند إنشاء إشعار يتم استدعاء NotificationGateway.sendNotification(). يتم العثور على الـ Socket الصحيح للمستخدم. يتم تنفيذ emit("receive-notification") بدون أي أخطاء. سجلات السيرفر تؤكد ذلك: Socket Connected: ... Online Users: [...] FOUND SOCKETS = [...] EMIT TO ... لكن في الواجهة الأمامية لا يتم استقبال حدث: receive-notification ولا يتم تنفيذ: socket.on("receive-notification", ...) الملفات التي أحتاج مراجعتها Frontend useNotification.ts socket/socket.ts useNotificationStore.ts useAuthStore.ts Backend notification.gateway.ts socket.adapter.ts notification.service.ts main.ts (خصوصًا تسجيل الـ WebSocket Adapter بواسطة app.useWebSocketAdapter(...)) ما تم التحقق منه الاتصال بالـ Socket ناجح. الـ Cookies تصل إلى الـ Socket Adapter. socket.data.user يتم تعبئته. المستخدم يُخزن داخل onlineUsers. sendNotification() يُستدعى. server.to(socketId).emit(...) يُنفذ. لكن الحدث لا يصل إلى React، لذلك أحتاج المساعدة في معرفة سبب عدم استقبال حدث receive-notification رغم نجاح جميع الخطوات السابقة. شكرًا لكم. Qr menu.zip اقتباس
0 Mustafa Suleiman نشر 10 أغسطس أرسل تقرير نشر 10 أغسطس في socket.ts، كل مرة يتم استدعاء connectSocket()، يتم إضافة listeners جديدة لـ connect و connect_error وذلك يسبب تراكم listeners ويسبب مشاكل. export const connectSocket = (): Socket => { const s = getSocket(); s.on("connect", () => { console.log("✅ SOCKET CONNECTED"); }); s.on("connect_error", (err) => { console.log("❌ SOCKET ERROR:", err.message); }); }; يجب إضافة listeners مرة واحدة فقط ومتابعة ذلك من خلال إنشاء متغير listenersAttached. وكذلك إضافة polling كـ fallback لإجبار Socket.IO على استخدام WebSocket فقط، وإن فشل الاتصال لثانية واحدة، لن يتم إعادة الاتصال بشكل صحيح. عليك بتعديل الأجزاء التالية في الكود فقط: import { io, Socket } from "socket.io-client"; import { useAuthStore } from "../stores/useAuthStore"; import { BACKEND_URL } from "../apis/request"; let socket: Socket | null = null; let listenersAttached = false; export const getSocket = (): Socket => { if (socket) return socket; socket = io(BACKEND_URL, { autoConnect: false, withCredentials: true, transports: ["websocket", "polling"], }); if (!listenersAttached) { socket.on("connect", () => { console.log("✅ SOCKET CONNECTED, ID:", socket?.id); }); socket.on("connect_error", (err) => { console.log("❌ SOCKET ERROR:", err.message); }); socket.on("disconnect", () => { console.log("🔌 SOCKET DISCONNECTED"); }); listenersAttached = true; } return socket; }; export const connectSocket = (): Socket => { const s = getSocket(); const user = useAuthStore.getState().user; if (!user) { console.warn("Socket connection blocked: no authenticated user"); return s; } if (!s.connected) { s.connect(); } return s; }; export const disconnectSocket = () => { if (socket?.connected) { socket.disconnect(); } socket = null; listenersAttached = false; }; export default getSocket; وفي useNotification.ts الـ User Reference يتغير في كل Render ولو s.user object يتم إنشاؤه من جديد في كل render، فإن الـ useEffect سيعيد تنفيذه في كل render وبالطبع يؤدي إلى إزالة وإضافة الـ listener بشكل مستمر. استخدام selector للحصول على user.id فقط بدلاً من user object: "use client"; import { useCallback, useEffect, useRef, useState } from "react"; import toast from "react-hot-toast"; import type { Socket } from "socket.io-client"; import { INotification } from "../types/notification"; import { notificationApi } from "../apis/notification"; import { useNotificationStore } from "../stores/useNotificationStore"; import { useAuthStore } from "../stores/useAuthStore"; import { connectSocket } from "../socket/socket"; export const useNotification = ( initialPage: number = 1, limit: number = 20, ) => { const { notifications, unreadCount, loading, isSocketConnected, setNotifications, setUnreadCount, setLoading, setSocketConnected, markAsReadLocal, markAllAsReadLocal, removeNotificationLocal, addIncomingNotification, } = useNotificationStore(); const [notification, setNotification] = useState<INotification | null>(null); const [page, setPage] = useState(initialPage); const socketRef = useRef<Socket | null>(null); const userId = useAuthStore((s) => s.user?._id); useEffect(() => { if (!userId) return; const socket = connectSocket(); socketRef.current = socket; const handleConnect = () => setSocketConnected(true); const handleDisconnect = () => setSocketConnected(false); const handleReceiveNotification = (incoming: INotification) => { console.log("🔥 RECEIVE NOTIFICATION", incoming); addIncomingNotification(incoming); toast.success(incoming.title || "لديك إشعار جديد"); }; const handleNotificationRead = (data: { notificationId: string }) => { markAsReadLocal(data.notificationId); }; socket.on("connect", handleConnect); socket.on("disconnect", handleDisconnect); socket.on("receive-notification", handleReceiveNotification); socket.on("notification-read", handleNotificationRead); if (socket.connected) { setSocketConnected(true); } return () => { socket.off("connect", handleConnect); socket.off("disconnect", handleDisconnect); socket.off("receive-notification", handleReceiveNotification); socket.off("notification-read", handleNotificationRead); }; }, [userId, addIncomingNotification, markAsReadLocal, setSocketConnected]); }; وفي notification.gateway.ts، تأكد من أن الـ userId في onlineUsers يطابق الـ userId في الإشعار بإضافة console.log: sendNotification(notification: { userId: string; }) { const sockets = getUserSockets(notification.userId); console.log('📨 Sending notification to userId:', notification.userId); console.log('🔌 Online users:', onlineUsers); console.log('🎯 Found sockets:', sockets); if (!sockets.length) { console.warn('⚠️ No sockets found for userId:', notification.userId); return; } sockets.forEach((socket) => { console.log('📤 Emitting to socket:', socket.socketId); this.server .to(socket.socketId) .emit('receive-notification', notification); }); } اقتباس
السؤال
Zen Eddin Allaham
السلام عليكم،
أواجه مشكلة في نظام Realtime Notifications باستخدام NestJS + Socket.IO + React (Next.js) ولم أتمكن من معرفة سببها.
وصف المشكلة
اتصال الـ Socket يتم بنجاح.
المستخدم يُضاف إلى قائمة onlineUsers.
عند إنشاء إشعار يتم استدعاء NotificationGateway.sendNotification().
يتم العثور على الـ Socket الصحيح للمستخدم.
يتم تنفيذ emit("receive-notification") بدون أي أخطاء.
سجلات السيرفر تؤكد ذلك:
Socket Connected: ... Online Users: [...] FOUND SOCKETS = [...] EMIT TO ...
لكن في الواجهة الأمامية لا يتم استقبال حدث:
receive-notification
ولا يتم تنفيذ:
socket.on("receive-notification", ...)
الملفات التي أحتاج مراجعتها
Frontend
useNotification.ts
socket/socket.ts
useNotificationStore.ts
useAuthStore.ts
Backend
notification.gateway.ts
socket.adapter.ts
notification.service.ts
main.ts (خصوصًا تسجيل الـ WebSocket Adapter بواسطة app.useWebSocketAdapter(...))
ما تم التحقق منه
الاتصال بالـ Socket ناجح.
الـ Cookies تصل إلى الـ Socket Adapter.
socket.data.user يتم تعبئته.
المستخدم يُخزن داخل onlineUsers.
sendNotification() يُستدعى.
server.to(socketId).emit(...) يُنفذ.
لكن الحدث لا يصل إلى React، لذلك أحتاج المساعدة في معرفة سبب عدم استقبال حدث receive-notification رغم نجاح جميع الخطوات السابقة.
شكرًا لكم.
Qr menu.zip
1 جواب على هذا السؤال
Recommended Posts
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.