feat: 用 SSE 替代 SWR 轮询,实现房间状态实时推送

SSE 断连时自动降级为 2s 轮询,重连后切回 SSE。
This commit is contained in:
2026-02-24 19:51:30 +08:00
parent f6949a062f
commit 8c0d89af6d
9 changed files with 223 additions and 71 deletions
+39 -12
View File
@@ -1,28 +1,55 @@
"use client";
import useSWR from "swr";
import { useRef } from "react";
import { useEffect, useRef } from "react";
import { RoomStatus } from "@/types";
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export function useRoomPolling(roomId: string) {
const settled = useRef(false);
const { data, error, isLoading, mutate } = useSWR<RoomStatus>(
`/api/room/${roomId}`,
fetcher,
{
refreshInterval: settled.current ? 0 : 1500,
revalidateOnFocus: true,
},
{ revalidateOnFocus: true },
);
if (data?.match != null) {
settled.current = true;
} else {
settled.current = false;
}
const fallbackRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => {
const es = new EventSource(`/api/room/${roomId}/events`);
es.onmessage = (e) => {
try {
const parsed = JSON.parse(e.data);
if (parsed.roomId) {
mutate(parsed, { revalidate: false });
}
} catch {
/* malformed message */
}
};
es.onerror = () => {
if (!fallbackRef.current) {
fallbackRef.current = setInterval(() => mutate(), 2000);
}
};
es.onopen = () => {
if (fallbackRef.current) {
clearInterval(fallbackRef.current);
fallbackRef.current = null;
}
};
return () => {
es.close();
if (fallbackRef.current) {
clearInterval(fallbackRef.current);
fallbackRef.current = null;
}
};
}, [roomId, mutate]);
return {
userCount: data?.userCount ?? 0,