Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lemon-facts-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': patch
---

Update how cache keys are created in SWR/RQ hooks.
278 changes: 130 additions & 148 deletions packages/shared/src/react/hooks/__tests__/usePagesOrInfinite.spec.ts

Large diffs are not rendered by default.

48 changes: 31 additions & 17 deletions packages/shared/src/react/hooks/createBillingPaginatedHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useUserContext,
} from '../contexts';
import type { PagesOrInfiniteOptions, PaginatedHookConfig, PaginatedResources } from '../types';
import { createCacheKeys } from './createCacheKeys';
import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite';

/**
Expand Down Expand Up @@ -35,6 +36,16 @@ export interface HookParams
* @default true
*/
enabled?: boolean;
/**
* On `cache` mode, no request will be triggered when the hook is mounted and the data will be fetched from the cache.
*
* @default undefined
*
* @hidden
*
* @experimental
*/
__experimental_mode?: 'cache';
}
> {
/**
Expand Down Expand Up @@ -110,28 +121,31 @@ export function createBillingPaginatedHook<TResource extends ClerkResource, TPar

const isEnabled = !!hookParams && clerk.loaded && !!billingEnabled && (externalEnabled ?? true);

const result = usePagesOrInfinite<TParams, ClerkPaginatedResponse<TResource>>(
(hookParams || {}) as TParams,
fetchFn,
{
...({
keepPreviousData: safeValues.keepPreviousData,
infinite: safeValues.infinite,
} as PaginatedHookConfig<unknown>),
const result = usePagesOrInfinite({
fetcher: fetchFn,
config: {
keepPreviousData: safeValues.keepPreviousData,
infinite: safeValues.infinite,
enabled: isEnabled,
...(options?.unauthenticated ? {} : { isSignedIn: Boolean(user) }),
__experimental_mode: safeValues.__experimental_mode,
initialPage: safeValues.initialPage,
pageSize: safeValues.pageSize,
},
{
type: resourceType,
...(options?.unauthenticated
? { for: safeFor }
: {
keys: createCacheKeys({
stablePrefix: resourceType,
authenticated: !options?.unauthenticated,
tracked: options?.unauthenticated
? ({ for: safeFor } as const)
: ({
userId: user?.id,
...(isForOrganization ? { orgId: organization?.id } : {}),
}),
},
);
...(isForOrganization ? { [__CLERK_USE_RQ__ ? 'orgId' : '_orgId']: organization?.id } : {}),
} as const),
untracked: {
args: hookParams as TParams,
},
}),
});

return result;
};
Expand Down
23 changes: 23 additions & 0 deletions packages/shared/src/react/hooks/createCacheKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @internal
*/
export function createCacheKeys<
Params,
StableKey extends string,
T extends Record<string, unknown> = Record<string, unknown>,
U extends Record<string, unknown> | undefined = undefined,
>(params: {
stablePrefix: StableKey;
authenticated: boolean;
tracked: T;
untracked: U extends { args: Params } ? U : never;

//U extends undefined ? never : U extends { args: Params } ? U['args'] : never;
}) {
return {
queryKey: [params.stablePrefix, params.authenticated, params.tracked, params.untracked] as const,
invalidationKey: [params.stablePrefix, params.authenticated, params.tracked] as const,
stableKey: params.stablePrefix,
authenticated: params.authenticated,
};
}
29 changes: 19 additions & 10 deletions packages/shared/src/react/hooks/useAPIKeys.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client';

import { eventMethodCalled } from '../../telemetry/events/method-called';
import type { APIKeyResource, ClerkPaginatedResponse, GetAPIKeysParams } from '../../types';
import type { APIKeyResource, GetAPIKeysParams } from '../../types';
import { useAssertWrappedByClerkProvider, useClerkInstanceContext } from '../contexts';
import type { PaginatedHookConfig, PaginatedResources } from '../types';
import { createCacheKeys } from './createCacheKeys';
import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite';

/**
Expand Down Expand Up @@ -92,17 +93,25 @@ export function useApiKeys<T extends UseApiKeysParams>(params?: T): UseApiKeysRe

const isEnabled = (safeValues.enabled ?? true) && clerk.loaded;

return usePagesOrInfinite<GetAPIKeysParams, ClerkPaginatedResponse<APIKeyResource>>(
hookParams,
clerk.apiKeys?.getAll ? (params: GetAPIKeysParams) => clerk.apiKeys.getAll(params) : undefined,
{
return usePagesOrInfinite({
fetcher: clerk.apiKeys?.getAll ? (params: GetAPIKeysParams) => clerk.apiKeys.getAll(params) : undefined,
config: {
keepPreviousData: safeValues.keepPreviousData,
infinite: safeValues.infinite,
enabled: isEnabled,
isSignedIn: Boolean(clerk.user),
initialPage: safeValues.initialPage,
pageSize: safeValues.pageSize,
},
{
type: 'apiKeys',
subject: safeValues.subject || '',
},
) as UseApiKeysReturn<T>;
keys: createCacheKeys({
stablePrefix: 'apiKeys',
authenticated: Boolean(clerk.user),
tracked: {
subject: safeValues.subject,
},
untracked: {
args: hookParams,
},
}),
}) as UseApiKeysReturn<T>;
}
115 changes: 69 additions & 46 deletions packages/shared/src/react/hooks/useOrganization.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { getCurrentOrganizationMembership } from '../../organization';
import { eventMethodCalled } from '../../telemetry/events/method-called';
import type {
ClerkPaginatedResponse,
GetDomainsParams,
GetInvitationsParams,
GetMembershipRequestParams,
Expand All @@ -19,6 +18,7 @@ import {
useSessionContext,
} from '../contexts';
import type { PaginatedHookConfig, PaginatedResources, PaginatedResourcesWithDefault } from '../types';
import { createCacheKeys } from './createCacheKeys';
import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite';

/**
Expand Down Expand Up @@ -357,70 +357,93 @@ export function useOrganization<T extends UseOrganizationParams>(params?: T): Us
status: invitationsSafeValues.status,
};

const domains = usePagesOrInfinite<GetDomainsParams, ClerkPaginatedResponse<OrganizationDomainResource>>(
{
...domainParams,
},
organization?.getDomains,
{
const domains = usePagesOrInfinite({
fetcher: organization?.getDomains,
config: {
keepPreviousData: domainSafeValues.keepPreviousData,
infinite: domainSafeValues.infinite,
enabled: !!domainParams,
isSignedIn: Boolean(organization),
initialPage: domainSafeValues.initialPage,
pageSize: domainSafeValues.pageSize,
},
{
type: 'domains',
organizationId: organization?.id,
},
);
keys: createCacheKeys({
stablePrefix: 'domains',
authenticated: Boolean(organization),
tracked: {
organizationId: organization?.id,
},
untracked: {
args: domainParams,
},
}),
});

const membershipRequests = usePagesOrInfinite<
GetMembershipRequestParams,
ClerkPaginatedResponse<OrganizationMembershipRequestResource>
>(
{
...membershipRequestParams,
},
organization?.getMembershipRequests,
{
const membershipRequests = usePagesOrInfinite({
fetcher: organization?.getMembershipRequests,
config: {
keepPreviousData: membershipRequestSafeValues.keepPreviousData,
infinite: membershipRequestSafeValues.infinite,
enabled: !!membershipRequestParams,
isSignedIn: Boolean(organization),
initialPage: membershipRequestSafeValues.initialPage,
pageSize: membershipRequestSafeValues.pageSize,
},
{
type: 'membershipRequests',
organizationId: organization?.id,
},
);
keys: createCacheKeys({
stablePrefix: 'membershipRequests',
authenticated: Boolean(organization),
tracked: {
organizationId: organization?.id,
},
untracked: {
args: membershipRequestParams,
},
}),
});

const memberships = usePagesOrInfinite<GetMembersParams, ClerkPaginatedResponse<OrganizationMembershipResource>>(
membersParams || {},
organization?.getMemberships,
{
const memberships = usePagesOrInfinite({
fetcher: organization?.getMemberships,
config: {
keepPreviousData: membersSafeValues.keepPreviousData,
infinite: membersSafeValues.infinite,
enabled: !!membersParams,
isSignedIn: Boolean(organization),
initialPage: membersSafeValues.initialPage,
pageSize: membersSafeValues.pageSize,
},
{
type: 'members',
organizationId: organization?.id,
},
);
keys: createCacheKeys({
stablePrefix: 'members',
authenticated: Boolean(organization),
tracked: {
organizationId: organization?.id,
},
untracked: {
args: membersParams,
},
}),
});

const invitations = usePagesOrInfinite<GetInvitationsParams, ClerkPaginatedResponse<OrganizationInvitationResource>>(
{
...invitationsParams,
},
organization?.getInvitations,
{
const invitations = usePagesOrInfinite({
fetcher: organization?.getInvitations,
config: {
keepPreviousData: invitationsSafeValues.keepPreviousData,
infinite: invitationsSafeValues.infinite,
enabled: !!invitationsParams,
isSignedIn: Boolean(organization),
initialPage: invitationsSafeValues.initialPage,
pageSize: invitationsSafeValues.pageSize,
},
{
type: 'invitations',
organizationId: organization?.id,
},
);
keys: createCacheKeys({
stablePrefix: 'invitations',
authenticated: Boolean(organization),
tracked: {
organizationId: organization?.id,
},
untracked: {
args: invitationsParams,
},
}),
});

if (organization === undefined) {
return {
Expand Down
Loading
Loading