feat: 用 SSE 替代 SWR 轮询,实现房间状态实时推送
SSE 断连时自动降级为 2s 轮询,重连后切回 SSE。
This commit is contained in:
+39
-12
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user