refactor: integrate useMounted hook to fix hydration error in BillingCard and CreditsBalanceCard

This commit is contained in:
javayhu 2025-07-13 22:59:26 +08:00
parent 49b39ad9dd
commit a8c76d3249
2 changed files with 16 additions and 13 deletions

View File

@ -13,6 +13,7 @@ import {
} from '@/components/ui/card'; } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton'; import { Skeleton } from '@/components/ui/skeleton';
import { getPricePlans } from '@/config/price-config'; import { getPricePlans } from '@/config/price-config';
import { useMounted } from '@/hooks/use-mounted';
import { usePayment } from '@/hooks/use-payment'; import { usePayment } from '@/hooks/use-payment';
import { LocaleLink, useLocaleRouter } from '@/i18n/navigation'; import { LocaleLink, useLocaleRouter } from '@/i18n/navigation';
import { authClient } from '@/lib/auth-client'; import { authClient } from '@/lib/auth-client';
@ -30,6 +31,7 @@ export default function BillingCard() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const localeRouter = useLocaleRouter(); const localeRouter = useLocaleRouter();
const hasHandledSession = useRef(false); const hasHandledSession = useRef(false);
const mounted = useMounted();
const { const {
isLoading: isLoadingPayment, isLoading: isLoadingPayment,
@ -72,10 +74,6 @@ export default function BillingCard() {
? formatDate(subscription.currentPeriodEnd) ? formatDate(subscription.currentPeriodEnd)
: null; : null;
// Determine if we are in a loading state
const isPageLoading = isLoadingPayment || isLoadingSession;
// console.log('billing card, isLoadingPayment', isLoadingPayment, 'isLoadingSession', isLoadingSession);
// Retry payment data fetching // Retry payment data fetching
const handleRetry = useCallback(() => { const handleRetry = useCallback(() => {
// console.log('handleRetry, refetch payment info'); // console.log('handleRetry, refetch payment info');
@ -97,8 +95,9 @@ export default function BillingCard() {
} }
}, [searchParams, localeRouter]); }, [searchParams, localeRouter]);
// Render loading skeleton // Render loading skeleton if not mounted or in a loading state
if (isPageLoading) { const isPageLoading = isLoadingPayment || isLoadingSession;
if (!mounted || isPageLoading) {
return ( return (
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}> <Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
<CardHeader> <CardHeader>
@ -108,10 +107,12 @@ export default function BillingCard() {
<CardDescription>{t('currentPlan.description')}</CardDescription> <CardDescription>{t('currentPlan.description')}</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4 flex-1"> <CardContent className="space-y-4 flex-1">
<div className="space-y-3"> <div className="flex items-center justify-start space-x-4">
<Skeleton className="h-6 w-1/2" /> <Skeleton className="h-6 w-1/5" />
<Skeleton className="h-6 w-4/5" /> </div>
<Skeleton className="h-6 w-4/5" /> <div className="text-sm text-muted-foreground space-y-2">
<Skeleton className="h-6 w-2/5" />
<Skeleton className="h-6 w-3/5" />
</div> </div>
</CardContent> </CardContent>
<CardFooter className="mt-2 px-6 py-4 flex justify-end items-center bg-background rounded-none"> <CardFooter className="mt-2 px-6 py-4 flex justify-end items-center bg-background rounded-none">
@ -148,8 +149,8 @@ export default function BillingCard() {
); );
} }
// currentPlanFromStore maybe null, so we need to check if it is null // currentPlan maybe null, so we need to check if it is null
if (!currentPlanFromStore) { if (!currentPlan) {
return ( return (
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}> <Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
<CardHeader> <CardHeader>

View File

@ -13,6 +13,7 @@ import {
import { Skeleton } from '@/components/ui/skeleton'; import { Skeleton } from '@/components/ui/skeleton';
import { websiteConfig } from '@/config/website'; import { websiteConfig } from '@/config/website';
import { useCredits } from '@/hooks/use-credits'; import { useCredits } from '@/hooks/use-credits';
import { useMounted } from '@/hooks/use-mounted';
import { usePayment } from '@/hooks/use-payment'; import { usePayment } from '@/hooks/use-payment';
import { LocaleLink, useLocaleRouter } from '@/i18n/navigation'; import { LocaleLink, useLocaleRouter } from '@/i18n/navigation';
import { formatDate } from '@/lib/formatter'; import { formatDate } from '@/lib/formatter';
@ -29,6 +30,7 @@ export default function CreditsBalanceCard() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const localeRouter = useLocaleRouter(); const localeRouter = useLocaleRouter();
const hasHandledSession = useRef(false); const hasHandledSession = useRef(false);
const mounted = useMounted();
// Use the credits hook to get balance // Use the credits hook to get balance
const { const {
@ -114,7 +116,7 @@ export default function CreditsBalanceCard() {
// Render loading skeleton // Render loading skeleton
const isPageLoading = isLoadingBalance || isLoadingStats; const isPageLoading = isLoadingBalance || isLoadingStats;
if (isPageLoading) { if (!mounted || isPageLoading) {
return ( return (
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}> <Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
<CardHeader> <CardHeader>