Skip to content

Commit 07a30ce

Browse files
authored
chore(shared): Refactor clearing cache in RQ hooks (#7330)
1 parent a11c2da commit 07a30ce

File tree

7 files changed

+102
-27
lines changed

7 files changed

+102
-27
lines changed

.changeset/smooth-lamps-raise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/shared': patch
3+
---
4+
5+
Refactor clearing cache in RQ hooks when a use signs out.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export function createBillingPaginatedHook<TResource extends ClerkResource, TPar
129129
keepPreviousData: safeValues.keepPreviousData,
130130
infinite: safeValues.infinite,
131131
enabled: isEnabled,
132-
...(options?.unauthenticated ? {} : { isSignedIn: Boolean(user) }),
132+
...(options?.unauthenticated ? {} : { isSignedIn: user !== null }),
133133
__experimental_mode: safeValues.__experimental_mode,
134134
initialPage: safeValues.initialPage,
135135
pageSize: safeValues.pageSize,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { useEffect, useRef } from 'react';
2+
3+
import { useClerkQueryClient } from '../clerk-rq/use-clerk-query-client';
4+
import { usePreviousValue } from './usePreviousValue';
5+
6+
export const withInfiniteKey = <T extends string>(key: T) => [key, `${key}-inf`] as const;
7+
8+
type ClearQueriesOnSignOutOptions = {
9+
isSignedOut: boolean;
10+
stableKeys: string | readonly string[];
11+
/**
12+
* Whether the queries for this hook are keyed as authenticated.
13+
* If this is not `true`, the effect becomes a no-op.
14+
*/
15+
authenticated?: boolean;
16+
/**
17+
* Optional callback that will run after queries are cleared on sign-out.
18+
*/
19+
onCleanup?: () => void;
20+
};
21+
22+
/**
23+
* Clears React Query caches associated with the given stable prefixes when
24+
* the authenticated state transitions from signed-in to signed-out.
25+
*
26+
* @internal
27+
*/
28+
export function useClearQueriesOnSignOut(options: ClearQueriesOnSignOutOptions) {
29+
const { isSignedOut, stableKeys, authenticated = true, onCleanup } = options;
30+
const stableKeysRef = useRef(stableKeys);
31+
32+
const [queryClient] = useClerkQueryClient();
33+
const previousIsSignedIn = usePreviousValue(!isSignedOut);
34+
35+
// If this hook's cache keys are not authenticated, skip all cleanup logic.
36+
37+
if (authenticated !== true) {
38+
return;
39+
}
40+
41+
// Calling this effect conditionally because we make sure that `authenticated` is always the same throughout the component lifecycle.
42+
// eslint-disable-next-line react-hooks/rules-of-hooks
43+
useEffect(() => {
44+
const isNowSignedOut = isSignedOut === true;
45+
46+
if (previousIsSignedIn && isNowSignedOut) {
47+
queryClient.removeQueries({
48+
predicate: query => {
49+
const [cachedStableKey, queryAuthenticated] = query.queryKey;
50+
51+
return (
52+
queryAuthenticated === true &&
53+
typeof cachedStableKey === 'string' &&
54+
(Array.isArray(stableKeysRef.current)
55+
? stableKeysRef.current.includes(cachedStableKey)
56+
: stableKeysRef.current === cachedStableKey)
57+
);
58+
},
59+
});
60+
61+
onCleanup?.();
62+
}
63+
}, [isSignedOut, previousIsSignedIn, queryClient]);
64+
}

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

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1+
import { useCallback, useMemo, useRef, useState } from 'react';
22

33
import type { ClerkPaginatedResponse } from '../../types';
44
import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
55
import { useClerkQueryClient } from '../clerk-rq/use-clerk-query-client';
66
import { useClerkInfiniteQuery } from '../clerk-rq/useInfiniteQuery';
77
import { useClerkQuery } from '../clerk-rq/useQuery';
88
import type { CacheSetter, ValueOrSetter } from '../types';
9+
import { useClearQueriesOnSignOut, withInfiniteKey } from './useClearQueriesOnSignOut';
910
import type { UsePagesOrInfiniteSignature } from './usePageOrInfinite.types';
1011
import { useWithSafeValues } from './usePagesOrInfinite.shared';
11-
import { usePreviousValue } from './usePreviousValue';
1212

1313
export const usePagesOrInfinite: UsePagesOrInfiniteSignature = params => {
1414
const { fetcher, config, keys } = params;
@@ -98,32 +98,18 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = params => {
9898
enabled: queriesEnabled && triggerInfinite,
9999
});
100100

101-
// Track previous isSignedIn state to detect sign-out transitions
102-
const previousIsSignedIn = usePreviousValue(isSignedIn);
103-
104-
// Detect sign-out and clear queries
105-
useEffect(() => {
106-
const isNowSignedOut = isSignedIn === false;
107-
108-
if (previousIsSignedIn && isNowSignedOut) {
109-
queryClient.removeQueries({
110-
predicate: query => {
111-
const [stablePrefix, authenticated] = query.queryKey;
112-
return (
113-
authenticated === true &&
114-
typeof stablePrefix === 'string' &&
115-
(stablePrefix === keys.queryKey[0] || stablePrefix === keys.queryKey[0] + '-inf')
116-
);
117-
},
118-
});
119-
101+
useClearQueriesOnSignOut({
102+
isSignedOut: isSignedIn === false,
103+
authenticated: keys.authenticated,
104+
stableKeys: withInfiniteKey(keys.stableKey),
105+
onCleanup: () => {
120106
// Reset paginated page to initial
121107
setPaginatedPage(initialPageRef.current);
122108

123109
// Force re-render to reflect cache changes
124110
void Promise.resolve().then(() => forceUpdate(n => n + 1));
125-
}
126-
}, [isSignedIn, queryClient, previousIsSignedIn, forceUpdate]);
111+
},
112+
});
127113

128114
// Compute data, count and page from the same data source to ensure consistency
129115
const computedValues = useMemo(() => {

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
22
import { useClerkQuery } from '../clerk-rq/useQuery';
33
import { useClerkInstanceContext, useOrganizationContext, useUserContext } from '../contexts';
44
import { useBillingHookEnabled } from './useBillingHookEnabled';
5+
import { useClearQueriesOnSignOut } from './useClearQueriesOnSignOut';
56
import { usePaymentAttemptQueryCacheKeys } from './usePaymentAttemptQuery.shared';
67
import type { PaymentAttemptQueryResult, UsePaymentAttemptQueryParams } from './usePaymentAttemptQuery.types';
78

@@ -17,7 +18,7 @@ function usePaymentAttemptQuery(params: UsePaymentAttemptQueryParams): PaymentAt
1718
const organizationId = forType === 'organization' ? (organization?.id ?? null) : null;
1819
const userId = user?.id ?? null;
1920

20-
const { queryKey } = usePaymentAttemptQueryCacheKeys({
21+
const { queryKey, stableKey, authenticated } = usePaymentAttemptQueryCacheKeys({
2122
paymentAttemptId,
2223
userId,
2324
orgId: organizationId,
@@ -28,6 +29,12 @@ function usePaymentAttemptQuery(params: UsePaymentAttemptQueryParams): PaymentAt
2829

2930
const queryEnabled = Boolean(paymentAttemptId) && billingEnabled;
3031

32+
useClearQueriesOnSignOut({
33+
isSignedOut: user === null, // works with the transitive state
34+
authenticated,
35+
stableKeys: stableKey,
36+
});
37+
3138
const query = useClerkQuery({
3239
queryKey,
3340
queryFn: ({ queryKey }) => {

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineKeepPreviousDataFn } from '../clerk-rq/keep-previous-data';
22
import { useClerkQuery } from '../clerk-rq/useQuery';
33
import { useClerkInstanceContext, useOrganizationContext, useUserContext } from '../contexts';
44
import { useBillingHookEnabled } from './useBillingHookEnabled';
5+
import { useClearQueriesOnSignOut } from './useClearQueriesOnSignOut';
56
import { useStatementQueryCacheKeys } from './useStatementQuery.shared';
67
import type { StatementQueryResult, UseStatementQueryParams } from './useStatementQuery.types';
78

@@ -17,7 +18,7 @@ function useStatementQuery(params: UseStatementQueryParams = {}): StatementQuery
1718
const organizationId = forType === 'organization' ? (organization?.id ?? null) : null;
1819
const userId = user?.id ?? null;
1920

20-
const { queryKey } = useStatementQueryCacheKeys({
21+
const { queryKey, stableKey, authenticated } = useStatementQueryCacheKeys({
2122
statementId,
2223
userId,
2324
orgId: organizationId,
@@ -28,6 +29,12 @@ function useStatementQuery(params: UseStatementQueryParams = {}): StatementQuery
2829

2930
const queryEnabled = Boolean(statementId) && billingEnabled;
3031

32+
useClearQueriesOnSignOut({
33+
isSignedOut: user === null,
34+
authenticated,
35+
stableKeys: stableKey,
36+
});
37+
3138
const query = useClerkQuery({
3239
queryKey,
3340
queryFn: () => {

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
useUserContext,
1212
} from '../contexts';
1313
import { useBillingHookEnabled } from './useBillingHookEnabled';
14+
import { useClearQueriesOnSignOut } from './useClearQueriesOnSignOut';
1415
import { useSubscriptionCacheKeys } from './useSubscription.shared';
1516
import type { SubscriptionResult, UseSubscriptionParams } from './useSubscription.types';
1617

@@ -37,13 +38,18 @@ export function useSubscription(params?: UseSubscriptionParams): SubscriptionRes
3738

3839
const [queryClient] = useClerkQueryClient();
3940

40-
const { queryKey, invalidationKey } = useSubscriptionCacheKeys({
41+
const { queryKey, invalidationKey, stableKey, authenticated } = useSubscriptionCacheKeys({
4142
userId: user?.id,
4243
orgId: organization?.id,
4344
for: params?.for,
4445
});
4546

4647
const queriesEnabled = Boolean(user?.id && billingEnabled);
48+
useClearQueriesOnSignOut({
49+
isSignedOut: user === null,
50+
authenticated,
51+
stableKeys: stableKey,
52+
});
4753

4854
const query = useClerkQuery({
4955
queryKey,

0 commit comments

Comments
 (0)