Skip to content

Commit cbe42db

Browse files
committed
cleanup
1 parent 42fda7b commit cbe42db

11 files changed

+122
-124
lines changed

packages/shared/src/react/billing/useInitializePaymentMethod.rq.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
55
import { useClerkQueryClient } from '../clerk-rq/use-clerk-query-client';
66
import { useClerkQuery } from '../clerk-rq/useQuery';
77
import { useOrganizationContext, useUserContext } from '../contexts';
8+
import { useBillingHookEnabled } from '../hooks/useBillingHookEnabled';
89

910
type InitializePaymentMethodOptions = {
1011
for?: ForPayerType;
@@ -22,17 +23,19 @@ export type UseInitializePaymentMethodResult = {
2223
* @internal
2324
*/
2425
function useInitializePaymentMethod(options?: InitializePaymentMethodOptions): UseInitializePaymentMethodResult {
25-
const { for: forType = 'user' } = options ?? {};
26+
const { for: forType } = options ?? {};
2627
const { organization } = useOrganizationContext();
2728
const user = useUserContext();
2829

2930
const resource = forType === 'organization' ? organization : user;
3031

32+
const billingEnabled = useBillingHookEnabled(options);
33+
3134
const queryKey = useMemo(() => {
32-
return ['billing-payment-method-initialize', { resourceId: resource?.id, for: forType }] as const;
33-
}, [resource?.id, forType]);
35+
return ['billing-payment-method-initialize', { resourceId: resource?.id }] as const;
36+
}, [resource?.id]);
3437

35-
const isEnabled = Boolean(resource?.id);
38+
const isEnabled = Boolean(resource?.id) && billingEnabled;
3639

3740
const query = useClerkQuery({
3841
queryKey,

packages/shared/src/react/billing/useStripeClerkLibs.rq.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { loadStripe } from '@stripe/stripe-js';
22

33
import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
44
import { useClerkQuery } from '../clerk-rq/useQuery';
5+
import { useBillingHookEnabled } from '../hooks/useBillingHookEnabled';
56
import { useClerk } from '../hooks/useClerk';
67

78
type LoadStripeFn = typeof loadStripe;
@@ -10,23 +11,24 @@ type StripeClerkLibs = {
1011
loadStripe: LoadStripeFn;
1112
};
1213

13-
export type UseStripeClerkLibsResult = StripeClerkLibs | null;
14-
1514
/**
1615
* This is the new implementation of the Stripe libraries loader using React Query.
1716
* It is exported only if the package is built with the `CLERK_USE_RQ` environment variable set to `true`.
1817
*
1918
* @internal
2019
*/
21-
function useStripeClerkLibs(): UseStripeClerkLibsResult {
20+
function useStripeClerkLibs(): StripeClerkLibs | null {
2221
const clerk = useClerk();
2322

23+
const billingEnabled = useBillingHookEnabled();
24+
2425
const query = useClerkQuery({
2526
queryKey: ['clerk-stripe-sdk'],
2627
queryFn: async () => {
2728
const loadStripe = (await clerk.__internal_loadStripeJs()) as LoadStripeFn;
2829
return { loadStripe };
2930
},
31+
enabled: billingEnabled,
3032
staleTime: Infinity,
3133
refetchOnWindowFocus: false,
3234
placeholderData: defineKeepPreviousDataFn(true),

packages/shared/src/react/billing/useStripeLoader.rq.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useMemo } from 'react';
33

44
import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
55
import { useClerkQuery } from '../clerk-rq/useQuery';
6+
import { useBillingHookEnabled } from '../hooks/useBillingHookEnabled';
67
import type { UseStripeClerkLibsResult } from './useStripeClerkLibs';
78

89
type StripeLoaderOptions = {
@@ -26,7 +27,9 @@ function useStripeLoader(options: StripeLoaderOptions): UseStripeLoaderResult {
2627
return ['stripe-sdk', { externalGatewayId, stripePublishableKey }] as const;
2728
}, [externalGatewayId, stripePublishableKey]);
2829

29-
const isEnabled = Boolean(stripeClerkLibs && externalGatewayId && stripePublishableKey);
30+
const billingEnabled = useBillingHookEnabled({ authenticated: true });
31+
32+
const isEnabled = Boolean(stripeClerkLibs && externalGatewayId && stripePublishableKey) && billingEnabled;
3033

3134
const query = useClerkQuery({
3235
queryKey,

packages/shared/src/react/hooks/createBillingPaginatedHook.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { eventMethodCalled } from '../../telemetry/events/method-called';
2-
import type { ClerkPaginatedResponse, ClerkResource, EnvironmentResource, ForPayerType } from '../../types';
2+
import type { ClerkPaginatedResponse, ClerkResource, ForPayerType } from '../../types';
33
import {
44
useAssertWrappedByClerkProvider,
55
useClerkInstanceContext,
@@ -9,6 +9,7 @@ import {
99
import type { ResourceCacheStableKey } from '../stable-keys';
1010
import type { PagesOrInfiniteOptions, PaginatedHookConfig, PaginatedResources } from '../types';
1111
import { createCacheKeys } from './createCacheKeys';
12+
import { useBillingHookEnabled } from './useBillingHookEnabled';
1213
import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite';
1314

1415
/**
@@ -98,15 +99,19 @@ export function createBillingPaginatedHook<TResource extends ClerkResource, TPar
9899

99100
const clerk = useClerkInstanceContext();
100101

101-
// @ts-expect-error `__unstable__environment` is not typed
102-
const environment = clerk.__unstable__environment as unknown as EnvironmentResource | null | undefined;
103102
const user = useUserContext();
104103
const { organization } = useOrganizationContext();
105104

106105
clerk.telemetry?.record(eventMethodCalled(hookName));
107106

108107
const isForOrganization = safeFor === 'organization';
109108

109+
const billingEnabled = useBillingHookEnabled({
110+
for: safeFor,
111+
enabled: externalEnabled,
112+
authenticated: !options?.unauthenticated,
113+
});
114+
110115
const hookParams =
111116
typeof paginationParams === 'undefined'
112117
? undefined
@@ -116,13 +121,9 @@ export function createBillingPaginatedHook<TResource extends ClerkResource, TPar
116121
...(options?.unauthenticated ? {} : isForOrganization ? { orgId: organization?.id } : {}),
117122
} as TParams);
118123

119-
const billingEnabled = isForOrganization
120-
? environment?.commerceSettings.billing.organization.enabled
121-
: environment?.commerceSettings.billing.user.enabled;
124+
const isEnabled = !!hookParams && clerk.loaded && !!billingEnabled;
122125

123-
const isEnabled = !!hookParams && clerk.loaded && !!billingEnabled && (externalEnabled ?? true);
124-
125-
const result = usePagesOrInfinite({
126+
return usePagesOrInfinite({
126127
fetcher: fetchFn,
127128
config: {
128129
keepPreviousData: safeValues.keepPreviousData,
@@ -147,7 +148,5 @@ export function createBillingPaginatedHook<TResource extends ClerkResource, TPar
147148
},
148149
}),
149150
});
150-
151-
return result;
152151
};
153152
}

packages/shared/src/react/hooks/createCacheKeys.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export function createCacheKeys<
2222
};
2323
}
2424

25+
/**
26+
* @internal
27+
*/
2528
export function toSWRQuery<T extends { queryKey: QueryKeyWithArgs<unknown> }>(keys: T) {
2629
const { queryKey } = keys;
2730
return {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { ForPayerType } from '../../types/billing';
2+
import { useClerkInstanceContext, useOrganizationContext, useUserContext } from '../contexts';
3+
4+
/**
5+
* @internal
6+
*/
7+
export function useBillingHookEnabled(params?: { for?: ForPayerType; enabled?: boolean; authenticated?: boolean }) {
8+
const clerk = useClerkInstanceContext();
9+
10+
const enabledFromParam = params?.enabled ?? true;
11+
12+
// @ts-expect-error `__unstable__environment` is not typed
13+
const environment = clerk.__unstable__environment as unknown as EnvironmentResource | null | undefined;
14+
15+
const user = useUserContext();
16+
const { organization } = useOrganizationContext();
17+
18+
const isOrganization = params?.for === 'organization';
19+
const billingEnabled = isOrganization
20+
? environment?.commerceSettings.billing.organization.enabled
21+
: environment?.commerceSettings.billing.user.enabled;
22+
23+
const requireUserAndOrganizationWhenAuthenticated =
24+
(params?.authenticated ?? true) ? (isOrganization ? Boolean(organization?.id) : true) && user?.id : true;
25+
26+
return billingEnabled && enabledFromParam && clerk.loaded && requireUserAndOrganizationWhenAuthenticated;
27+
}

packages/shared/src/react/hooks/usePagesOrInfinite.rq.tsx

Lines changed: 43 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,52 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = params => {
127127
}
128128
}, [isSignedIn, queryClient, previousIsSignedIn, forceUpdate]);
129129

130-
const page = useMemo(() => {
130+
// Compute data, count and page from the same data source to ensure consistency
131+
const computedValues = useMemo(() => {
131132
if (triggerInfinite) {
132133
// Read from query data first, fallback to cache
133134
const cachedData = queryClient.getQueryData<{ pages?: Array<ClerkPaginatedResponse<any>> }>(infiniteQueryKey);
134-
const pages = infiniteQuery.data?.pages ?? cachedData?.pages ?? [];
135-
// Return pages.length if > 0, otherwise return initialPage (default 1)
136-
return pages.length > 0 ? pages.length : initialPageRef.current;
135+
const pages = queriesEnabled ? (infiniteQuery.data?.pages ?? cachedData?.pages ?? []) : (cachedData?.pages ?? []);
136+
137+
// Ensure pages is always an array and filter out null/undefined pages
138+
const validPages = Array.isArray(pages) ? pages.filter(Boolean) : [];
139+
140+
return {
141+
data:
142+
validPages
143+
.map((a: ClerkPaginatedResponse<any>) => a?.data)
144+
.flat()
145+
.filter(Boolean) ?? [],
146+
count: validPages[validPages.length - 1]?.total_count ?? 0,
147+
page: validPages.length > 0 ? validPages.length : initialPageRef.current,
148+
};
137149
}
138-
return paginatedPage;
139-
}, [triggerInfinite, infiniteQuery.data?.pages, paginatedPage, queryClient, infiniteQueryKey]);
150+
151+
// When query is disabled (via enabled flag), the hook's data is stale, so only read from cache
152+
// This ensures that after cache clearing, we return consistent empty state
153+
const pageData = queriesEnabled
154+
? (singlePageQuery.data ?? queryClient.getQueryData<ClerkPaginatedResponse<any>>(pagesQueryKey))
155+
: queryClient.getQueryData<ClerkPaginatedResponse<any>>(pagesQueryKey);
156+
157+
return {
158+
data: Array.isArray(pageData?.data) ? pageData.data : [],
159+
count: typeof pageData?.total_count === 'number' ? pageData.total_count : 0,
160+
page: paginatedPage,
161+
};
162+
// eslint-disable-next-line react-hooks/exhaustive-deps -- forceUpdateCounter is used to trigger re-renders for cache updates
163+
}, [
164+
queriesEnabled,
165+
forceUpdateCounter,
166+
triggerInfinite,
167+
infiniteQuery.data?.pages,
168+
singlePageQuery.data,
169+
queryClient,
170+
infiniteQueryKey,
171+
pagesQueryKey,
172+
paginatedPage,
173+
]);
174+
175+
const { data, count, page } = computedValues;
140176

141177
const fetchPage: ValueOrSetter<number> = useCallback(
142178
numberOrgFn => {
@@ -157,56 +193,6 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = params => {
157193
[infiniteQuery, page, triggerInfinite, queryClient, infiniteQueryKey],
158194
);
159195

160-
const data = useMemo(() => {
161-
if (triggerInfinite) {
162-
const cachedData = queryClient.getQueryData<{ pages?: Array<ClerkPaginatedResponse<any>> }>(infiniteQueryKey);
163-
// When query is disabled, the hook's data is stale, so only read from cache
164-
const pages = queriesEnabled ? (infiniteQuery.data?.pages ?? cachedData?.pages ?? []) : (cachedData?.pages ?? []);
165-
return pages.map((a: ClerkPaginatedResponse<any>) => a?.data).flat() ?? [];
166-
}
167-
168-
// When query is disabled (via enabled flag), the hook's data is stale, so only read from cache
169-
// This ensures that after cache clearing, we return empty data
170-
const pageData = queriesEnabled
171-
? (singlePageQuery.data ?? queryClient.getQueryData<ClerkPaginatedResponse<any>>(pagesQueryKey))
172-
: queryClient.getQueryData<ClerkPaginatedResponse<any>>(pagesQueryKey);
173-
return pageData?.data ?? [];
174-
}, [
175-
queriesEnabled,
176-
forceUpdateCounter,
177-
triggerInfinite,
178-
singlePageQuery.data,
179-
infiniteQuery.data,
180-
queryClient,
181-
pagesQueryKey,
182-
infiniteQueryKey,
183-
]);
184-
185-
const count = useMemo(() => {
186-
if (triggerInfinite) {
187-
const cachedData = queryClient.getQueryData<{ pages?: Array<ClerkPaginatedResponse<any>> }>(infiniteQueryKey);
188-
// When query is disabled, the hook's data is stale, so only read from cache
189-
const pages = queriesEnabled ? (infiniteQuery.data?.pages ?? cachedData?.pages ?? []) : (cachedData?.pages ?? []);
190-
return pages[pages.length - 1]?.total_count || 0;
191-
}
192-
193-
// When query is disabled (via enabled flag), the hook's data is stale, so only read from cache
194-
// This ensures that after cache clearing, we return 0
195-
const pageData = queriesEnabled
196-
? (singlePageQuery.data ?? queryClient.getQueryData<ClerkPaginatedResponse<any>>(pagesQueryKey))
197-
: queryClient.getQueryData<ClerkPaginatedResponse<any>>(pagesQueryKey);
198-
return pageData?.total_count ?? 0;
199-
}, [
200-
queriesEnabled,
201-
forceUpdateCounter,
202-
triggerInfinite,
203-
singlePageQuery.data,
204-
infiniteQuery.data,
205-
queryClient,
206-
pagesQueryKey,
207-
infiniteQueryKey,
208-
]);
209-
210196
const isLoading = triggerInfinite ? infiniteQuery.isLoading : singlePageQuery.isLoading;
211197
const isFetching = triggerInfinite ? infiniteQuery.isFetching : singlePageQuery.isFetching;
212198
const error = (triggerInfinite ? infiniteQuery.error : singlePageQuery.error) ?? null;
@@ -246,7 +232,7 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = params => {
246232
>;
247233
return { ...prevValue, pages: nextPages };
248234
});
249-
// Force re-render to reflect cache changes
235+
// Force immediate re-render to reflect cache changes
250236
forceUpdate(n => n + 1);
251237
return Promise.resolve();
252238
}

packages/shared/src/react/hooks/usePaymentAttemptQuery.rq.tsx

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,22 @@
1-
import { eventMethodCalled } from '../../telemetry/events';
21
import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
32
import { useClerkQuery } from '../clerk-rq/useQuery';
4-
import {
5-
useAssertWrappedByClerkProvider,
6-
useClerkInstanceContext,
7-
useOrganizationContext,
8-
useUserContext,
9-
} from '../contexts';
3+
import { useClerkInstanceContext, useOrganizationContext, useUserContext } from '../contexts';
4+
import { useBillingHookEnabled } from './useBillingHookEnabled';
105
import { usePaymentAttemptQueryCacheKeys } from './usePaymentAttemptQuery.shared';
116
import type { PaymentAttemptQueryResult, UsePaymentAttemptQueryParams } from './usePaymentAttemptQuery.types';
127

13-
const HOOK_NAME = 'usePaymentAttemptQuery';
14-
158
/**
169
* This is the new implementation of usePaymentAttemptQuery using React Query.
1710
* It is exported only if the package is built with the `CLERK_USE_RQ` environment variable set to `true`.
1811
*
1912
* @internal
2013
*/
2114
function usePaymentAttemptQuery(params: UsePaymentAttemptQueryParams): PaymentAttemptQueryResult {
22-
useAssertWrappedByClerkProvider(HOOK_NAME);
23-
24-
const { paymentAttemptId, enabled = true, keepPreviousData = false, for: forType = 'user' } = params;
15+
const { paymentAttemptId, keepPreviousData = false, for: forType = 'user' } = params;
2516
const clerk = useClerkInstanceContext();
2617
const user = useUserContext();
2718
const { organization } = useOrganizationContext();
2819

29-
clerk.telemetry?.record(eventMethodCalled(HOOK_NAME));
30-
3120
const organizationId = forType === 'organization' ? (organization?.id ?? null) : null;
3221
const userId = user?.id ?? null;
3322

@@ -38,7 +27,9 @@ function usePaymentAttemptQuery(params: UsePaymentAttemptQueryParams): PaymentAt
3827
for: forType,
3928
});
4029

41-
const queryEnabled = Boolean(paymentAttemptId) && enabled && (forType !== 'organization' || Boolean(organizationId));
30+
const billingEnabled = useBillingHookEnabled(params);
31+
32+
const queryEnabled = Boolean(paymentAttemptId) && billingEnabled;
4233

4334
const query = useClerkQuery({
4435
queryKey,

packages/shared/src/react/hooks/usePlanDetailsQuery.rq.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
1-
import { eventMethodCalled } from '../../telemetry/events';
21
import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
32
import { useClerkQuery } from '../clerk-rq/useQuery';
4-
import { useAssertWrappedByClerkProvider, useClerkInstanceContext } from '../contexts';
3+
import { useClerkInstanceContext } from '../contexts';
4+
import { useBillingHookEnabled } from './useBillingHookEnabled';
55
import { usePlanDetailsQueryCacheKeys } from './usePlanDetailsQuery.shared';
66
import type { PlanDetailsQueryResult, UsePlanDetailsQueryParams } from './usePlanDetailsQuery.types';
77

8-
const HOOK_NAME = 'usePlanDetailsQuery';
9-
108
/**
119
* This is the new implementation of usePlanDetailsQuery using React Query.
1210
* It is exported only if the package is built with the `CLERK_USE_RQ` environment variable set to `true`.
1311
*
1412
* @internal
1513
*/
1614
export function __internal_usePlanDetailsQuery(params: UsePlanDetailsQueryParams = {}): PlanDetailsQueryResult {
17-
useAssertWrappedByClerkProvider(HOOK_NAME);
18-
19-
const { planId, initialPlan = null, enabled = true, keepPreviousData = true } = params;
15+
const { planId, initialPlan = null, keepPreviousData = true } = params;
2016
const clerk = useClerkInstanceContext();
2117

22-
clerk.telemetry?.record(eventMethodCalled(HOOK_NAME));
23-
2418
const targetPlanId = planId ?? initialPlan?.id ?? null;
2519

2620
const { queryKey } = usePlanDetailsQueryCacheKeys({ planId: targetPlanId });
2721

28-
const queryEnabled = Boolean(targetPlanId) && enabled;
22+
const billingEnabled = useBillingHookEnabled({
23+
authenticated: false,
24+
});
25+
26+
const queryEnabled = Boolean(targetPlanId) && billingEnabled;
2927

3028
const query = useClerkQuery({
3129
queryKey,

0 commit comments

Comments
 (0)