perf: streamline billing panel bootstrap

Remove the redundant billing account read, cache packages for one minute, prefetch the billing chunk, and parallelize independent package reads.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jesse-ux
2026-09-24 18:22:44 +08:00
co-authored by Claude Code
parent e1452aaea5
commit 2f53fa0171
7 changed files with 134 additions and 41 deletions
+10 -15
View File
@@ -10,6 +10,7 @@ import {
type MembershipPlanAlias,
type MembershipProduct,
} from "@/lib/membership";
import { fetchPaymentPackages } from "@/lib/payment-packages-cache";
export type BillingAccount = {
user: { id: string; email: string | null };
@@ -137,18 +138,14 @@ export function useBillingPanel(input: {
const fetchPackages = useCallback(async () => {
try {
const response = await fetch("/api/payment/packages", { cache: "no-store" });
const payload = await response.json().catch(() => null);
if (response.ok && payload?.enabled === true) {
setPaymentEnabled(true);
setPaymentPackages(payload.packages || []);
const payload = await fetchPaymentPackages((next) => {
setPaymentEnabled(next.enabled);
setPaymentPackages(next.packages);
setPackagesError("");
return;
}
if (!response.ok) {
setPackagesError("套餐支付暂时不可用,请稍后重试");
resetPaymentState();
}
});
setPaymentEnabled(payload.enabled);
setPaymentPackages(payload.packages);
setPackagesError("");
} catch {
setPackagesError("套餐支付暂时不可用,请稍后重试");
resetPaymentState();
@@ -181,10 +178,8 @@ export function useBillingPanel(input: {
}, []);
useEffect(() => {
void (async () => {
await Promise.allSettled([fetchAccountData(), fetchPackages()]);
})();
}, [fetchAccountData, fetchPackages]);
void fetchPackages();
}, [fetchPackages]);
useEffect(() => {
if (!input.highlightedPlan || planHighlightScrolled.current || paymentPackages.length === 0) return;