From b5b4bb9d66c9c175f4956990c320541ec13335d1 Mon Sep 17 00:00:00 2001 From: Noa Virellia Date: Thu, 1 Jan 2026 10:38:57 +0800 Subject: [PATCH] refactor(client): optimize suspense components Signed-off-by: Noa Virellia --- client/src/components/app-sidebar.tsx | 4 +- client/src/components/hoc/with-fallback.tsx | 20 +++++++++ client/src/components/nav-user.tsx | 21 ++++++++- .../workbenchCards/card-skeleton.tsx | 9 ++++ .../src/components/workbenchCards/checkin.tsx | 44 ++++++++++--------- client/src/routes/__root.tsx | 19 +++++++- client/src/routes/_sidebarLayout/index.tsx | 16 +++---- 7 files changed, 99 insertions(+), 34 deletions(-) create mode 100644 client/src/components/hoc/with-fallback.tsx create mode 100644 client/src/components/workbenchCards/card-skeleton.tsx diff --git a/client/src/components/app-sidebar.tsx b/client/src/components/app-sidebar.tsx index 518ac82..801d55c 100644 --- a/client/src/components/app-sidebar.tsx +++ b/client/src/components/app-sidebar.tsx @@ -7,7 +7,6 @@ import * as React from 'react'; import NixOSLogo from '@/assets/nixos.svg?react'; import { NavMain } from '@/components/nav-main'; import { NavSecondary } from '@/components/nav-secondary'; -import { NavUser } from '@/components/nav-user'; import { Sidebar, SidebarContent, @@ -17,6 +16,7 @@ import { SidebarMenuButton, SidebarMenuItem, } from '@/components/ui/sidebar'; +import { NavUser } from './nav-user'; const data = { user: { @@ -48,7 +48,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { diff --git a/client/src/components/hoc/with-fallback.tsx b/client/src/components/hoc/with-fallback.tsx new file mode 100644 index 0000000..e4e8679 --- /dev/null +++ b/client/src/components/hoc/with-fallback.tsx @@ -0,0 +1,20 @@ +import type { ReactNode } from 'react'; +import React, { Suspense } from 'react'; + +export function withFallback

( + Component: React.ComponentType

, + fallback: ReactNode, +) { + const Wrapped: React.FC

= (props) => { + return ( + + + + ); + }; + + Wrapped.displayName = `withFallback(${Component.displayName! || Component.name || 'Component' + })`; + + return Wrapped; +} diff --git a/client/src/components/nav-user.tsx b/client/src/components/nav-user.tsx index d21cba2..9f6c5dd 100644 --- a/client/src/components/nav-user.tsx +++ b/client/src/components/nav-user.tsx @@ -24,8 +24,10 @@ import { } from '@/components/ui/sidebar'; import { useUserInfo } from '@/hooks/data/useUserInfo'; import { useLogout } from '@/hooks/useLogout'; +import { withFallback } from './hoc/with-fallback'; +import { Skeleton } from './ui/skeleton'; -export function NavUser() { +function NavUser_() { const { isMobile } = useSidebar(); const { data: user } = useUserInfo(); const { logout } = useLogout(); @@ -83,3 +85,20 @@ export function NavUser() { ); } + +function NavUserSkeleton() { + return ( + + +

+ + +
+ +
+ ); +} + +export const NavUser = withFallback(NavUser_, ); diff --git a/client/src/components/workbenchCards/card-skeleton.tsx b/client/src/components/workbenchCards/card-skeleton.tsx new file mode 100644 index 0000000..007b499 --- /dev/null +++ b/client/src/components/workbenchCards/card-skeleton.tsx @@ -0,0 +1,9 @@ +import { Skeleton } from '../ui/skeleton'; + +export function CardSkeleton() { + return ( + + ); +} diff --git a/client/src/components/workbenchCards/checkin.tsx b/client/src/components/workbenchCards/checkin.tsx index 45f84aa..fcfb34a 100644 --- a/client/src/components/workbenchCards/checkin.tsx +++ b/client/src/components/workbenchCards/checkin.tsx @@ -2,29 +2,31 @@ import { Badge } from '@/components/ui/badge'; import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; import { useUserInfo } from '@/hooks/data/useUserInfo'; import { QrDialog } from '../checkin/qr-dialog'; +import { withFallback } from '../hoc/with-fallback'; +import { CardSkeleton } from './card-skeleton'; -export function CheckinCard() { +function CheckinCard_() { const { data } = useUserInfo(); return ( - <> - - - 签到状态 - - {data.checkin !== null ? '已签到' : '未签到'} - {data.checkin !== null && {`${new Date(data.checkin).toLocaleString()}`}} - - - Day 1 - - - - - - - - + + + 签到状态 + + {data.checkin !== null ? '已签到' : '未签到'} + {data.checkin !== null && {`${new Date(data.checkin).toLocaleString()}`}} + + + Day 1 + + + + + + + ); } + +export const CheckinCard = withFallback(CheckinCard_, ); diff --git a/client/src/routes/__root.tsx b/client/src/routes/__root.tsx index 6a5987c..faf535a 100644 --- a/client/src/routes/__root.tsx +++ b/client/src/routes/__root.tsx @@ -4,7 +4,24 @@ import { ThemeProvider } from '@/components/theme-provider'; import { Toaster } from '@/components/ui/sonner'; import '@/index.css'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: (failureCount, error: any) => { + // eslint-disable-next-line ts/no-unsafe-assignment + const status + // eslint-disable-next-line ts/no-unsafe-member-access + = error?.response?.status ?? error?.status; + + if (status >= 400 && status < 500) { + return false; + } + + return failureCount < 3; + }, + }, + }, +}); function RootLayout() { return ( diff --git a/client/src/routes/_sidebarLayout/index.tsx b/client/src/routes/_sidebarLayout/index.tsx index e23ff0f..9a8b7d8 100644 --- a/client/src/routes/_sidebarLayout/index.tsx +++ b/client/src/routes/_sidebarLayout/index.tsx @@ -13,18 +13,16 @@ export const Route = createFileRoute('/_sidebarLayout/')({ }, }); -function SectionCards() { - return ( -
- -
- ); -} - function Index() { return (
- + {/* Section Cards */} +
+ +
); }