perf: use websocket context

This commit is contained in:
hamster1963 2024-11-24 11:32:42 +08:00
parent 56d6305096
commit 5364d5cdb5
7 changed files with 81 additions and 34 deletions

View File

@ -6,7 +6,6 @@ import { formatNezhaInfo, formatRelativeTime } from "@/lib/utils";
import { NezhaAPI, NezhaAPIResponse } from "@/types/nezha-api"; import { NezhaAPI, NezhaAPIResponse } from "@/types/nezha-api";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import useWebSocket from "react-use-websocket";
import { import {
Area, Area,
AreaChart, AreaChart,
@ -18,6 +17,7 @@ import {
} from "recharts"; } from "recharts";
import { ServerDetailChartLoading } from "./loading/ServerDetailLoading"; import { ServerDetailChartLoading } from "./loading/ServerDetailLoading";
import AnimatedCircularProgressBar from "./ui/animated-circular-progress-bar"; import AnimatedCircularProgressBar from "./ui/animated-circular-progress-bar";
import { useWebSocketContext } from "@/hooks/use-websocket-context";
type cpuChartData = { type cpuChartData = {
timeStamp: string; timeStamp: string;
@ -54,16 +54,11 @@ type connectChartData = {
export default function ServerDetailChart() { export default function ServerDetailChart() {
const { id } = useParams(); const { id } = useParams();
const { lastMessage, readyState } = useWebSocket("/api/v1/ws/server", { const { lastMessage, readyState } = useWebSocketContext();
shouldReconnect: () => true,
reconnectInterval: 3000,
});
// 检查连接状态 // 检查连接状态
if (readyState !== 1) { if (readyState !== 1) {
return ( return <ServerDetailChartLoading />;
<ServerDetailChartLoading />
);
} }
// 解析消息 // 解析消息

View File

@ -3,24 +3,19 @@ import { ServerDetailLoading } from "@/components/loading/ServerDetailLoading";
import ServerFlag from "@/components/ServerFlag"; import ServerFlag from "@/components/ServerFlag";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { useWebSocketContext } from "@/hooks/use-websocket-context";
import { cn, formatBytes, formatNezhaInfo } from "@/lib/utils"; import { cn, formatBytes, formatNezhaInfo } from "@/lib/utils";
import { NezhaAPIResponse } from "@/types/nezha-api"; import { NezhaAPIResponse } from "@/types/nezha-api";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import useWebSocket from "react-use-websocket";
export default function ServerDetailOverview() { export default function ServerDetailOverview() {
const navigate = useNavigate(); const navigate = useNavigate();
const { id } = useParams(); const { id } = useParams();
const { lastMessage, readyState } = useWebSocket("/api/v1/ws/server", { const { lastMessage, readyState } = useWebSocketContext();
shouldReconnect: () => true,
reconnectInterval: 3000,
});
// 检查连接状态 // 检查连接状态
if (readyState !== 1) { if (readyState !== 1) {
return ( return <ServerDetailLoading />;
<ServerDetailLoading />
);
} }
// 解析消息 // 解析消息

View File

@ -0,0 +1,11 @@
import { createContext } from "react";
export interface WebSocketContextType {
sendMessage: (message: string) => void;
lastMessage: MessageEvent | null;
readyState: number;
}
export const WebSocketContext = createContext<WebSocketContextType | undefined>(
undefined,
);

View File

@ -0,0 +1,34 @@
import React from "react";
import {
WebSocketContext,
type WebSocketContextType,
} from "./websocket-context";
import useWebSocket from "react-use-websocket";
interface WebSocketProviderProps {
url: string;
children: React.ReactNode;
}
export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({
url,
children,
}) => {
const { sendMessage, lastMessage, readyState } = useWebSocket(url, {
reconnectAttempts: 10,
reconnectInterval: 3000,
shouldReconnect: () => true,
});
const contextValue: WebSocketContextType = {
sendMessage,
lastMessage,
readyState,
};
return (
<WebSocketContext.Provider value={contextValue}>
{children}
</WebSocketContext.Provider>
);
};

View File

@ -0,0 +1,12 @@
import { useContext } from "react";
import { WebSocketContext } from "../context/websocket-context";
export const useWebSocketContext = () => {
const context = useContext(WebSocketContext);
if (context === undefined) {
throw new Error(
"useWebSocketContext must be used within a WebSocketProvider",
);
}
return context;
};

View File

@ -7,6 +7,7 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import { MotionProvider } from "./components/motion/motion-provider"; import { MotionProvider } from "./components/motion/motion-provider";
import { WebSocketProvider } from "./context/websocket-provider";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@ -15,19 +16,21 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<MotionProvider> <MotionProvider>
<ThemeProvider storageKey="vite-ui-theme"> <ThemeProvider storageKey="vite-ui-theme">
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<App /> <WebSocketProvider url="/api/v1/ws/server">
<Toaster <App />
duration={1000} <Toaster
toastOptions={{ duration={1000}
classNames: { toastOptions={{
default: classNames: {
"w-fit rounded-full px-2.5 py-1.5 bg-neutral-100 border border-neutral-200 backdrop-blur-xl shadow-none", default:
}, "w-fit rounded-full px-2.5 py-1.5 bg-neutral-100 border border-neutral-200 backdrop-blur-xl shadow-none",
}} },
position="top-center" }}
className={"flex items-center justify-center"} position="top-center"
/> className={"flex items-center justify-center"}
<ReactQueryDevtools /> />
<ReactQueryDevtools />
</WebSocketProvider>
</QueryClientProvider> </QueryClientProvider>
</ThemeProvider> </ThemeProvider>
</MotionProvider> </MotionProvider>

View File

@ -1,4 +1,3 @@
import useWebSocket from "react-use-websocket";
import { NezhaAPIResponse } from "@/types/nezha-api"; import { NezhaAPIResponse } from "@/types/nezha-api";
import ServerCard from "@/components/ServerCard"; import ServerCard from "@/components/ServerCard";
import { formatNezhaInfo } from "@/lib/utils"; import { formatNezhaInfo } from "@/lib/utils";
@ -9,16 +8,14 @@ import { useQuery } from "@tanstack/react-query";
import { fetchServerGroup } from "@/lib/nezha-api"; import { fetchServerGroup } from "@/lib/nezha-api";
import GroupSwitch from "@/components/GroupSwitch"; import GroupSwitch from "@/components/GroupSwitch";
import { ServerGroup } from "@/types/nezha-api"; import { ServerGroup } from "@/types/nezha-api";
import { useWebSocketContext } from "@/hooks/use-websocket-context";
export default function Servers() { export default function Servers() {
const { data: groupData } = useQuery({ const { data: groupData } = useQuery({
queryKey: ["server-group"], queryKey: ["server-group"],
queryFn: () => fetchServerGroup(), queryFn: () => fetchServerGroup(),
}); });
const { lastMessage, readyState } = useWebSocket("/api/v1/ws/server", { const { lastMessage, readyState } = useWebSocketContext();
shouldReconnect: () => true,
reconnectInterval: 3000,
});
// 添加分组状态 // 添加分组状态
const [currentGroup, setCurrentGroup] = useState<string>("All"); const [currentGroup, setCurrentGroup] = useState<string>("All");