@@ -6,6 +6,7 @@ import { useSWR, useSWRInfinite } from '../clerk-swr';
66import type { CacheSetter , ValueOrSetter } from '../types' ;
77import type { UsePagesOrInfiniteSignature } from './usePageOrInfinite.types' ;
88import { getDifferentKeys , useWithSafeValues } from './usePagesOrInfinite.shared' ;
9+ import { usePreviousValue } from './usePreviousValue' ;
910
1011const cachingSWROptions = {
1112 dedupingInterval : 1000 * 60 ,
@@ -32,8 +33,35 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = (params, fetcher,
3233 pageSize : pageSizeRef . current ,
3334 } ;
3435
36+ const previousIsSignedIn = usePreviousValue ( isSignedIn ) ;
37+
38+ // cacheMode being `true` indicates that the cache key is defined, but the fetcher is not.
39+ // This allows to ready the cache instead of firing a request.
3540 const shouldFetch = ! triggerInfinite && enabled && ( ! cacheMode ? ! ! fetcher : true ) ;
36- const swrKey = isSignedIn ? pagesCacheKey : shouldFetch ? pagesCacheKey : null ;
41+
42+ // Attention:
43+ //
44+ // This complex logic is necessary to ensure that the cached data is not used when the user is signed out.
45+ // `useSWR` with `key` set to `null` and `keepPreviousData` set to `true` will return the previous cached data until the hook unmounts.
46+ // So for hooks that render authenticated data, we need to ensure that the cached data is not used when the user is signed out.
47+ //
48+ // 1. Fetcher should not fire if user is signed out on mount. (fetcher does not run, loading states are not triggered)
49+ // 2. If user was signed in and then signed out, cached data should become null. (fetcher runs and returns null, loading states are triggered)
50+ //
51+ // We achieve (2) by setting the key to the cache key when the user transitions to signed out and forcing the fetcher to return null.
52+ const swrKey =
53+ typeof isSignedIn === 'boolean'
54+ ? previousIsSignedIn === true && isSignedIn === false
55+ ? pagesCacheKey
56+ : isSignedIn
57+ ? shouldFetch
58+ ? pagesCacheKey
59+ : null
60+ : null
61+ : shouldFetch
62+ ? pagesCacheKey
63+ : null ;
64+
3765 const swrFetcher =
3866 ! cacheMode && ! ! fetcher
3967 ? ( cacheKeyParams : Record < string , unknown > ) => {
@@ -53,6 +81,22 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = (params, fetcher,
5381 mutate : swrMutate ,
5482 } = useSWR ( swrKey , swrFetcher , { keepPreviousData, ...cachingSWROptions } ) ;
5583
84+ // Attention:
85+ //
86+ // Cache behavior for infinite loading when signing out:
87+ //
88+ // Unlike `useSWR` above (which requires complex transition handling), `useSWRInfinite` has simpler sign-out semantics:
89+ // 1. When user is signed out on mount, the key getter returns `null`, preventing any fetches.
90+ // 2. When user transitions from signed in to signed out, the key getter returns `null` for all page indices.
91+ // 3. When `useSWRInfinite`'s key getter returns `null`, SWR will not fetch data and considers that page invalid.
92+ // 4. Unlike paginated mode, `useSWRInfinite` does not support `keepPreviousData`, so there's no previous data retention.
93+ //
94+ // This simpler behavior works because:
95+ // - `useSWRInfinite` manages multiple pages internally, each with its own cache key
96+ // - When the key getter returns `null`, all page fetches are prevented and pages become invalid
97+ // - Without `keepPreviousData`, the hook will naturally reflect the empty/invalid state
98+ //
99+ // Result: No special transition logic needed - just return `null` from key getter when `isSignedIn === false`.
56100 const {
57101 data : swrInfiniteData ,
58102 isLoading : swrInfiniteIsLoading ,
@@ -63,7 +107,7 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = (params, fetcher,
63107 mutate : swrInfiniteMutate ,
64108 } = useSWRInfinite (
65109 pageIndex => {
66- if ( ! triggerInfinite || ! enabled ) {
110+ if ( ! triggerInfinite || ! enabled || isSignedIn === false ) {
67111 return null ;
68112 }
69113
@@ -75,9 +119,9 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = (params, fetcher,
75119 } ;
76120 } ,
77121 cacheKeyParams => {
78- // @ts -ignore - swr provider passes back cacheKey object, compute fetcher params
122+ // @ts -ignore - remove cache-only keys from request params
79123 const requestParams = getDifferentKeys ( cacheKeyParams , cacheKeys ) ;
80- // @ts -ignore - params narrowing deferred to fetcher time
124+ // @ts -ignore - fetcher expects Params subset; narrowing at call-site
81125 return fetcher ?.( requestParams ) ;
82126 } ,
83127 cachingSWROptions ,
@@ -160,7 +204,9 @@ export const usePagesOrInfinite: UsePagesOrInfiniteSignature = (params, fetcher,
160204 fetchPrevious,
161205 hasNextPage,
162206 hasPreviousPage,
207+ // Let the hook return type define this type
163208 revalidate : revalidate as any ,
209+ // Let the hook return type define this type
164210 setData : setData as any ,
165211 } ;
166212} ;
0 commit comments