From e20e16a02fcaeaec9428ef72f5c68548716a29f1 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 19 Sep 2023 14:58:59 +0300 Subject: [PATCH 01/10] feat(shared): Expose stable mutate --- .changeset/blue-ghosts-float.md | 6 ++ .../src/react/hooks/useOrganization.tsx | 50 ++++++++++--- .../src/react/hooks/useOrganizationList.tsx | 70 ++++++++++++------- .../src/react/hooks/usePagesOrInfinite.ts | 40 ++++++----- packages/shared/src/react/types.ts | 13 ++-- 5 files changed, 120 insertions(+), 59 deletions(-) create mode 100644 .changeset/blue-ghosts-float.md diff --git a/.changeset/blue-ghosts-float.md b/.changeset/blue-ghosts-float.md new file mode 100644 index 00000000000..19edafa659f --- /dev/null +++ b/.changeset/blue-ghosts-float.md @@ -0,0 +1,6 @@ +--- +'@clerk/shared': patch +--- + +Expose mutate for paginated lists of data in organization hooks. +`const {userMemberships:{mutate}} = useOrganizationList({userMemberships:true})` diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx index 1e0fe64340e..6178a3cfb35 100644 --- a/packages/shared/src/react/hooks/useOrganization.tsx +++ b/packages/shared/src/react/hooks/useOrganization.tsx @@ -56,7 +56,9 @@ type UseOrganizationParams = { }); }; -type UseOrganizationReturn = +type UseOrganization = ( + params?: T, +) => | { isLoaded: false; organization: undefined; @@ -103,14 +105,36 @@ type UseOrganizationReturn = */ membershipList: OrganizationMembershipResource[] | null | undefined; membership: OrganizationMembershipResource | null | undefined; - domains: PaginatedResources | null; - membershipRequests: PaginatedResources | null; - memberships: PaginatedResources | null; - invitations: PaginatedResources | null; + domains: PaginatedResources< + OrganizationDomainResource, + T['membershipRequests'] extends { infinite: true } ? true : false, + T['membershipRequests'] extends { infinite: true } + ? ClerkPaginatedResponse + : OrganizationDomainResource[] + > | null; + membershipRequests: PaginatedResources< + OrganizationMembershipRequestResource, + T['membershipRequests'] extends { infinite: true } ? true : false, + T['membershipRequests'] extends { infinite: true } + ? ClerkPaginatedResponse + : OrganizationMembershipRequestResource[] + > | null; + memberships: PaginatedResources< + OrganizationMembershipResource, + T['memberships'] extends { infinite: true } ? true : false, + T['memberships'] extends { infinite: true } + ? ClerkPaginatedResponse + : OrganizationMembershipResource[] + > | null; + invitations: PaginatedResources< + OrganizationInvitationResource, + T['invitations'] extends { infinite: true } ? true : false, + T['invitations'] extends { infinite: true } + ? ClerkPaginatedResponse + : OrganizationInvitationResource[] + > | null; }; -type UseOrganization = (params?: UseOrganizationParams) => UseOrganizationReturn; - const undefinedPaginatedResource = { data: undefined, count: undefined, @@ -124,6 +148,7 @@ const undefinedPaginatedResource = { fetchPrevious: undefined, hasNextPage: false, hasPreviousPage: false, + mutate: undefined, } as const; export const useOrganization: UseOrganization = params => { @@ -371,10 +396,13 @@ export const useOrganization: UseOrganization = params => { void mutateMembershipList(); void mutateInvitationList(); }, - domains, - membershipRequests, - memberships, - invitations, + // Let the hook return type define this type + domains: domains as any, + // Let the hook return type define this type + membershipRequests: membershipRequests as any, + // Let the hook return type define this type + memberships: memberships as any, + invitations: invitations as any, }; }; diff --git a/packages/shared/src/react/hooks/useOrganizationList.tsx b/packages/shared/src/react/hooks/useOrganizationList.tsx index beae119aa12..b4daa034bed 100644 --- a/packages/shared/src/react/hooks/useOrganizationList.tsx +++ b/packages/shared/src/react/hooks/useOrganizationList.tsx @@ -38,8 +38,25 @@ type UseOrganizationListParams = { }; type OrganizationList = ReturnType; +const undefinedPaginatedResource = { + data: undefined, + count: undefined, + isLoading: false, + isFetching: false, + isError: false, + page: undefined, + pageCount: undefined, + fetchPage: undefined, + fetchNext: undefined, + fetchPrevious: undefined, + hasNextPage: false, + hasPreviousPage: false, + mutate: undefined, +} as const; -type UseOrganizationListReturn = +type UseOrganizationList = ( + params?: T, +) => | { isLoaded: false; /** @@ -60,29 +77,29 @@ type UseOrganizationListReturn = organizationList: OrganizationList; createOrganization: (params: CreateOrganizationParams) => Promise; setActive: SetActive; - userMemberships: PaginatedResources; - userInvitations: PaginatedResources; - userSuggestions: PaginatedResources; + userMemberships: PaginatedResources< + OrganizationMembershipResource, + T['userMemberships'] extends { infinite: true } ? true : false, + T['userMemberships'] extends { infinite: true } + ? ClerkPaginatedResponse + : OrganizationMembershipResource[] + >; + userInvitations: PaginatedResources< + UserOrganizationInvitationResource, + T['userInvitations'] extends { infinite: true } ? true : false, + T['userInvitations'] extends { infinite: true } + ? ClerkPaginatedResponse + : UserOrganizationInvitationResource[] + >; + userSuggestions: PaginatedResources< + OrganizationSuggestionResource, + T['userSuggestions'] extends { infinite: true } ? true : false, + T['userSuggestions'] extends { infinite: true } + ? ClerkPaginatedResponse + : OrganizationSuggestionResource[] + >; }; -const undefinedPaginatedResource = { - data: undefined, - count: undefined, - isLoading: false, - isFetching: false, - isError: false, - page: undefined, - pageCount: undefined, - fetchPage: undefined, - fetchNext: undefined, - fetchPrevious: undefined, - hasNextPage: false, - hasPreviousPage: false, - unstable__mutate: undefined, -} as const; - -type UseOrganizationList = (params?: UseOrganizationListParams) => UseOrganizationListReturn; - export const useOrganizationList: UseOrganizationList = params => { const { userMemberships, userInvitations, userSuggestions } = params || {}; @@ -216,9 +233,12 @@ export const useOrganizationList: UseOrganizationList = params => { organizationList: createOrganizationList(user.organizationMemberships), setActive: clerk.setActive, createOrganization: clerk.createOrganization, - userMemberships: memberships, - userInvitations: invitations, - userSuggestions: suggestions, + // Let the hook return type define this type + userMemberships: memberships as any, + // Let the hook return type define this type + userInvitations: invitations as any, + // Let the hook return type define this type + userSuggestions: suggestions as any, }; deprecatedObjectProperty(result, 'organizationList', 'Use `userMemberships` instead.'); diff --git a/packages/shared/src/react/hooks/usePagesOrInfinite.ts b/packages/shared/src/react/hooks/usePagesOrInfinite.ts index be8e12f281e..b9f823b60e7 100644 --- a/packages/shared/src/react/hooks/usePagesOrInfinite.ts +++ b/packages/shared/src/react/hooks/usePagesOrInfinite.ts @@ -55,10 +55,26 @@ export const useWithSafeValues = (params: T | type ArrayType = DataArray extends Array ? ElementType : never; type ExtractData = Type extends { data: infer Data } ? ArrayType : Type; +type DefaultOptions = { + /** + * Persists the previous pages with new ones in the same array + */ + infinite?: boolean; + /** + * Return the previous key's data until the new data has been loaded + */ + keepPreviousData?: boolean; + /** + * Should a request be triggered + */ + enabled?: boolean; +}; + type UsePagesOrInfinite = < Params extends PagesOrInfiniteOptions, FetcherReturnData extends Record, CacheKeys = Record, + TOptions extends DefaultOptions = DefaultOptions, >( /** * The parameters will be passed to the fetcher @@ -71,24 +87,9 @@ type UsePagesOrInfinite = < /** * Internal configuration of the hook */ - options: { - /** - * Persists the previous pages with new ones in the same array - */ - infinite?: boolean; - /** - * Return the previous key's data until the new data has been loaded - */ - keepPreviousData?: boolean; - /** - * Should a request be triggered - */ - enabled?: boolean; - }, + options: TOptions, cacheKeys: CacheKeys, -) => PaginatedResources> & { - unstable__mutate: () => Promise; -}; +) => PaginatedResources, TOptions['infinite'], FetcherReturnData>; export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, cacheKeys) => { const [paginatedPage, setPaginatedPage] = useState(params.initialPage ?? 1); @@ -206,7 +207,7 @@ export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, const hasNextPage = count - offsetCount * pageSizeRef.current > page * pageSizeRef.current; const hasPreviousPage = (page - 1) * pageSizeRef.current > offsetCount * pageSizeRef.current; - const unstable__mutate = triggerInfinite ? swrInfiniteMutate : swrMutate; + const mutate = triggerInfinite ? swrInfiniteMutate : swrMutate; return { data, @@ -221,6 +222,7 @@ export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, fetchPrevious, hasNextPage, hasPreviousPage, - unstable__mutate, + // Let the hook return type define this type + mutate: mutate as any, }; }; diff --git a/packages/shared/src/react/types.ts b/packages/shared/src/react/types.ts index a2614cb214d..acf7091c047 100644 --- a/packages/shared/src/react/types.ts +++ b/packages/shared/src/react/types.ts @@ -1,5 +1,7 @@ +import type { KeyedMutator } from './clerk-swr'; + export type ValueOrSetter = (size: T | ((_size: T) => T)) => void; -export type PaginatedResources = { +export type PaginatedResources = { data: T[]; count: number; isLoading: boolean; @@ -12,11 +14,14 @@ export type PaginatedResources = { fetchNext: () => void; hasNextPage: boolean; hasPreviousPage: boolean; + mutate: Infinite extends true + ? // Array of pages of data + KeyedMutator<(ArrayOrPaginated | undefined)[]> + : // Array of data + KeyedMutator; }; // Utility type to convert PaginatedDataAPI to properties as undefined, except booleans set to false export type PaginatedResourcesWithDefault = { - [K in keyof PaginatedResources]: PaginatedResources[K] extends boolean - ? false - : PaginatedResources[K] | undefined; + [K in keyof PaginatedResources]: PaginatedResources[K] extends boolean ? false : undefined; }; From 870b9d42f80732f103b5aadaaa811a8a47ffd036 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 19 Sep 2023 15:02:42 +0300 Subject: [PATCH 02/10] chore(clerk-js): OrganizationList and switcher with stable mutate --- .../OrganizationList/UserInvitationList.tsx | 9 +++- .../OrganizationList/UserSuggestionList.tsx | 10 ++++- .../UserInvitationSuggestionList.tsx | 18 ++++++-- .../components/OrganizationSwitcher/utils.ts | 44 ++++++++----------- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx b/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx index c32931a39a0..4415dc90f8d 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx @@ -5,7 +5,7 @@ import { useCoreClerk, useCoreOrganizationList } from '../../contexts'; import { localizationKeys } from '../../customizables'; import { useCardState, withCardStateProvider } from '../../elements'; import { handleError } from '../../utils'; -import { updateCacheInPlace } from '../OrganizationSwitcher/utils'; +import { populateCacheUpdateItem } from '../OrganizationSwitcher/utils'; import { PreviewListItem, PreviewListItemButton } from './shared'; import { MembershipPreview } from './UserMembershipList'; import { organizationListParams } from './utils'; @@ -39,7 +39,12 @@ export const InvitationPreview = withCardStateProvider((props: UserOrganizationI }) .then(([updatedItem, organization]) => { // Update cache in case another listener depends on it - updateCacheInPlace(userInvitations)(updatedItem); + userInvitations?.mutate?.(pages => populateCacheUpdateItem(updatedItem, pages), { + // Since `accept` gives back the updated information, + // we don't need to revalidate here. + revalidate: false, + }); + setAcceptedOrganization(organization); }) .catch(err => handleError(err, [], card.setError)); diff --git a/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx b/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx index 7536a87ca19..ab4504a609e 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx @@ -4,7 +4,7 @@ import { useCoreOrganizationList } from '../../contexts'; import { localizationKeys, Text } from '../../customizables'; import { useCardState, withCardStateProvider } from '../../elements'; import { handleError } from '../../utils'; -import { updateCacheInPlace } from '../OrganizationSwitcher/utils'; +import { populateCacheUpdateItem } from '../OrganizationSwitcher/utils'; import { PreviewListItem, PreviewListItemButton } from './shared'; import { organizationListParams } from './utils'; @@ -17,7 +17,13 @@ export const AcceptRejectInvitationButtons = (props: OrganizationSuggestionResou const handleAccept = () => { return card .runAsync(props.accept) - .then(updateCacheInPlace(userSuggestions)) + .then(updatedItem => { + userSuggestions?.mutate?.(pages => populateCacheUpdateItem(updatedItem, pages), { + // Since `accept` gives back the updated information, + // we don't need to revalidate here. + revalidate: false, + }); + }) .catch(err => handleError(err, [], card.setError)); }; diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx index fac892d69cf..13d152a9518 100644 --- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx @@ -9,7 +9,7 @@ import { useInView } from '../../hooks'; import type { PropsOfComponent } from '../../styledSystem'; import { common } from '../../styledSystem'; import { handleError } from '../../utils'; -import { organizationListParams, removeItemFromPaginatedCache, updateCacheInPlace } from './utils'; +import { organizationListParams, populateCacheRemoveItem, populateCacheUpdateItem } from './utils'; const useFetchInvitations = () => { const { userInvitations, userSuggestions } = useCoreOrganizationList(organizationListParams); @@ -44,7 +44,13 @@ const AcceptRejectSuggestionButtons = (props: OrganizationSuggestionResource) => const handleAccept = () => { return card .runAsync(props.accept) - .then(updateCacheInPlace(userSuggestions)) + .then(updatedItem => { + userSuggestions?.mutate?.(pages => populateCacheUpdateItem(updatedItem, pages), { + // Since `accept` gives back the updated information, + // we don't need to revalidate here. + revalidate: false, + }); + }) .catch(err => handleError(err, [], card.setError)); }; @@ -81,7 +87,13 @@ const AcceptRejectInvitationButtons = (props: UserOrganizationInvitationResource const handleAccept = () => { return card .runAsync(props.accept) - .then(removeItemFromPaginatedCache(userInvitations)) + .then(updatedItem => { + userInvitations?.mutate?.(pages => populateCacheRemoveItem(updatedItem, pages), { + // Since `accept` gives back the updated information, + // we don't need to revalidate here. + revalidate: false, + }); + }) .catch(err => handleError(err, [], card.setError)); }; diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts b/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts index 7a9c81e3c8f..7f49d4ee0b6 100644 --- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts +++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts @@ -17,9 +17,16 @@ export const organizationListParams = { export const populateCacheUpdateItem = ( updatedItem: T, - itemsInfinitePages: ClerkPaginatedResponse[], + itemsInfinitePages: (ClerkPaginatedResponse | undefined)[] | undefined, ) => { + if (typeof itemsInfinitePages === 'undefined') { + return [{ data: [updatedItem], total_count: 1 }]; + } + return itemsInfinitePages.map(item => { + if (typeof item === 'undefined') { + return item; + } const newData = item.data.map(obj => { if (obj.id === updatedItem.id) { return { @@ -33,38 +40,23 @@ export const populateCacheUpdateItem = ( }); }; -export const updateCacheInPlace = - (userSuggestions: any) => - (result: T): any => { - userSuggestions?.unstable__mutate?.(result, { - populateCache: populateCacheUpdateItem, - // Since `accept` gives back the updated information, - // we don't need to revalidate here. - revalidate: false, - }); - }; - export const populateCacheRemoveItem = ( updatedItem: T, - itemsInfinitePages: ClerkPaginatedResponse[], + itemsInfinitePages: (ClerkPaginatedResponse | undefined)[] | undefined, ) => { - const prevTotalCount = itemsInfinitePages[itemsInfinitePages.length - 1].total_count; + const prevTotalCount = itemsInfinitePages?.[itemsInfinitePages.length - 1]?.total_count; - return itemsInfinitePages.map(item => { + if (!prevTotalCount) { + return undefined; + } + + return itemsInfinitePages?.map(item => { + if (typeof item === 'undefined') { + return item; + } const newData = item.data.filter(obj => { return obj.id !== updatedItem.id; }); return { ...item, data: newData, total_count: prevTotalCount - 1 }; }); }; - -export const removeItemFromPaginatedCache = - (userInvitations: any) => - (result: T): any => { - userInvitations?.unstable__mutate?.(result, { - populateCache: populateCacheRemoveItem, - // Since `accept` gives back the updated information, - // we don't need to revalidate here. - revalidate: false, - }); - }; From 612268a7e65ad7ad092bba93ae164fe62bc090a5 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 19 Sep 2023 15:03:00 +0300 Subject: [PATCH 03/10] chore(clerk-js): OrganizationProfile with stable mutate --- .../OrganizationProfile/ActiveMembersList.tsx | 5 ++--- .../OrganizationProfile/RemoveDomainPage.tsx | 2 +- .../OrganizationProfile/RequestToJoinList.tsx | 12 +++++++++--- .../OrganizationProfile/VerifiedDomainPage.tsx | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx index 66f12915354..5ffd9d13d05 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx @@ -27,7 +27,7 @@ export const ActiveMembersList = () => { const handleRoleChange = (membership: OrganizationMembershipResource) => (newRole: MembershipRole) => { return card .runAsync(async () => { - await membership.update({ role: newRole }); + return await membership.update({ role: newRole }); }) .catch(err => handleError(err, [], card.setError)); }; @@ -35,8 +35,7 @@ export const ActiveMembersList = () => { const handleRemove = (membership: OrganizationMembershipResource) => () => { return card .runAsync(async () => { - const destroyedMembership = await membership.destroy(); - return destroyedMembership; + return await membership.destroy(); }) .then(mutateSwrState) .catch(err => handleError(err, [], card.setError)); diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx index 64339007f89..00bcd3e303c 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx @@ -66,7 +66,7 @@ export const RemoveDomainPage = () => { successMessage={localizationKeys('organizationProfile.removeDomainPage.successMessage', { domain: ref.current?.name, })} - deleteResource={() => domain?.delete().then(() => (domains as any).unstable__mutate())} + deleteResource={() => domain?.delete().then(() => domains?.mutate?.())} breadcrumbTitle={localizationKeys('organizationProfile.profilePage.domainSection.title')} Breadcrumbs={OrganizationProfileBreadcrumbs} /> diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx index 1ec22ef807a..0054046e7ce 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx @@ -49,23 +49,29 @@ const RequestRow = withCardStateProvider( (props: { request: OrganizationMembershipRequestResource; onError: ReturnType['setError'] }) => { const { request, onError } = props; const card = useCardState(); - const { membershipRequests } = useCoreOrganization({ + const { membership, membershipRequests } = useCoreOrganization({ membershipRequests: membershipRequestsParams, }); const onAccept = () => { + if (!membership || !membershipRequests) { + return; + } return card .runAsync(async () => { await request.accept(); - await (membershipRequests as any).unstable__mutate?.(); + await membershipRequests.mutate(); }, 'accept') .catch(err => handleError(err, [], onError)); }; const onReject = () => { + if (!membership || !membershipRequests) { + return; + } return card .runAsync(async () => { await request.reject(); - await (membershipRequests as any).unstable__mutate?.(); + await membershipRequests.mutate(); }, 'reject') .catch(err => handleError(err, [], onError)); }; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx index b9be72731c5..f1f49029771 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx @@ -55,7 +55,7 @@ export const VerifiedDomainPage = withCardStateProvider(() => { const card = useCardState(); const { organizationSettings } = useEnvironment(); - const { organization, domains } = useCoreOrganization({ + const { membership, organization, domains } = useCoreOrganization({ domains: { infinite: true, }, @@ -147,7 +147,7 @@ export const VerifiedDomainPage = withCardStateProvider(() => { }); const updateEnrollmentMode = async () => { - if (!domain || !organization) { + if (!domain || !organization || !membership || !domains) { return; } @@ -157,7 +157,7 @@ export const VerifiedDomainPage = withCardStateProvider(() => { deletePending: deletePending.checked, }); - await (domains as any).unstable__mutate(); + await domains.mutate(); await navigate('../../'); } catch (e) { From 60474fdb17ce576cb44a8000f2a29ce75b257bbf Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 27 Sep 2023 12:50:03 +0300 Subject: [PATCH 04/10] fix(shared): Replace mutate with setCache and revalidate --- .../src/react/hooks/useOrganization.tsx | 3 ++- .../src/react/hooks/useOrganizationList.tsx | 3 ++- .../src/react/hooks/usePagesOrInfinite.ts | 21 ++++++++++++++----- packages/shared/src/react/types.ts | 14 ++++++++----- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx index 6178a3cfb35..45b89e61dbe 100644 --- a/packages/shared/src/react/hooks/useOrganization.tsx +++ b/packages/shared/src/react/hooks/useOrganization.tsx @@ -148,7 +148,8 @@ const undefinedPaginatedResource = { fetchPrevious: undefined, hasNextPage: false, hasPreviousPage: false, - mutate: undefined, + revalidate: undefined, + setCache: undefined, } as const; export const useOrganization: UseOrganization = params => { diff --git a/packages/shared/src/react/hooks/useOrganizationList.tsx b/packages/shared/src/react/hooks/useOrganizationList.tsx index b4daa034bed..3a07dd96118 100644 --- a/packages/shared/src/react/hooks/useOrganizationList.tsx +++ b/packages/shared/src/react/hooks/useOrganizationList.tsx @@ -51,7 +51,8 @@ const undefinedPaginatedResource = { fetchPrevious: undefined, hasNextPage: false, hasPreviousPage: false, - mutate: undefined, + revalidate: undefined, + setCache: undefined, } as const; type UseOrganizationList = ( diff --git a/packages/shared/src/react/hooks/usePagesOrInfinite.ts b/packages/shared/src/react/hooks/usePagesOrInfinite.ts index b9f823b60e7..134765dea9d 100644 --- a/packages/shared/src/react/hooks/usePagesOrInfinite.ts +++ b/packages/shared/src/react/hooks/usePagesOrInfinite.ts @@ -2,9 +2,8 @@ import { useCallback, useMemo, useRef, useState } from 'react'; -import { useSWR, useSWRInfinite } from '../clerk-swr'; -import type { ValueOrSetter } from '../types'; -import type { PaginatedResources } from '../types'; +import { useSWR, useSWRInfinite } from './clerk-swr'; +import type { CacheSetter, PaginatedResources, ValueOrSetter } from './types'; function getDifferentKeys(obj1: Record, obj2: Record): Record { const keysSet = new Set(Object.keys(obj2)); @@ -207,7 +206,17 @@ export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, const hasNextPage = count - offsetCount * pageSizeRef.current > page * pageSizeRef.current; const hasPreviousPage = (page - 1) * pageSizeRef.current > offsetCount * pageSizeRef.current; - const mutate = triggerInfinite ? swrInfiniteMutate : swrMutate; + const setCache: CacheSetter = triggerInfinite + ? value => + swrInfiniteMutate(value, { + revalidate: false, + }) + : value => + swrMutate(value, { + revalidate: false, + }); + + const revalidate = triggerInfinite ? () => swrInfiniteMutate() : () => swrMutate(); return { data, @@ -223,6 +232,8 @@ export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, hasNextPage, hasPreviousPage, // Let the hook return type define this type - mutate: mutate as any, + revalidate: revalidate as any, + // Let the hook return type define this type + setCache: setCache as any, }; }; diff --git a/packages/shared/src/react/types.ts b/packages/shared/src/react/types.ts index acf7091c047..a428df867bd 100644 --- a/packages/shared/src/react/types.ts +++ b/packages/shared/src/react/types.ts @@ -1,6 +1,9 @@ -import type { KeyedMutator } from './clerk-swr'; - export type ValueOrSetter = (size: T | ((_size: T) => T)) => void; + +export type CacheSetter = ( + data?: CData | ((currentData?: CData) => Promise | undefined | CData), +) => Promise; + export type PaginatedResources = { data: T[]; count: number; @@ -14,11 +17,12 @@ export type PaginatedResources void; hasNextPage: boolean; hasPreviousPage: boolean; - mutate: Infinite extends true + revalidate: () => Promise; + setCache: Infinite extends true ? // Array of pages of data - KeyedMutator<(ArrayOrPaginated | undefined)[]> + CacheSetter<(ArrayOrPaginated | undefined)[]> : // Array of data - KeyedMutator; + CacheSetter; }; // Utility type to convert PaginatedDataAPI to properties as undefined, except booleans set to false From deb7ea49af17aff6a3577c1ea14e987546611730 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 27 Sep 2023 12:51:21 +0300 Subject: [PATCH 05/10] fix(clerk-js): Replace mutate with setCache and revalidate --- .../OrganizationList/UserInvitationList.tsx | 7 +------ .../OrganizationList/UserSuggestionList.tsx | 8 +------- .../OrganizationProfile/ActiveMembersList.tsx | 12 +++--------- .../OrganizationProfile/RemoveDomainPage.tsx | 2 +- .../OrganizationProfile/RequestToJoinList.tsx | 4 ++-- .../OrganizationProfile/VerifiedDomainPage.tsx | 2 +- .../UserInvitationSuggestionList.tsx | 16 ++-------------- 7 files changed, 11 insertions(+), 40 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx b/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx index 4415dc90f8d..dd2faa8f6cd 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx @@ -39,12 +39,7 @@ export const InvitationPreview = withCardStateProvider((props: UserOrganizationI }) .then(([updatedItem, organization]) => { // Update cache in case another listener depends on it - userInvitations?.mutate?.(pages => populateCacheUpdateItem(updatedItem, pages), { - // Since `accept` gives back the updated information, - // we don't need to revalidate here. - revalidate: false, - }); - + void userInvitations?.setCache?.(cachedPages => populateCacheUpdateItem(updatedItem, cachedPages)); setAcceptedOrganization(organization); }) .catch(err => handleError(err, [], card.setError)); diff --git a/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx b/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx index ab4504a609e..587d92e3e9e 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx @@ -17,13 +17,7 @@ export const AcceptRejectInvitationButtons = (props: OrganizationSuggestionResou const handleAccept = () => { return card .runAsync(props.accept) - .then(updatedItem => { - userSuggestions?.mutate?.(pages => populateCacheUpdateItem(updatedItem, pages), { - // Since `accept` gives back the updated information, - // we don't need to revalidate here. - revalidate: false, - }); - }) + .then(updatedItem => userSuggestions?.setCache?.(pages => populateCacheUpdateItem(updatedItem, pages))) .catch(err => handleError(err, [], card.setError)); }; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx index 5ffd9d13d05..6a23ac4b640 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx @@ -13,13 +13,6 @@ export const ActiveMembersList = () => { memberships: true, }); - const mutateSwrState = () => { - const unstable__mutate = (rest as any).unstable__mutate; - if (unstable__mutate && typeof unstable__mutate === 'function') { - unstable__mutate(); - } - }; - if (!organization) { return null; } @@ -35,9 +28,10 @@ export const ActiveMembersList = () => { const handleRemove = (membership: OrganizationMembershipResource) => () => { return card .runAsync(async () => { - return await membership.destroy(); + const destroyedMembership = await membership.destroy(); + await memberships?.revalidate?.(); + return destroyedMembership; }) - .then(mutateSwrState) .catch(err => handleError(err, [], card.setError)); }; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx index 00bcd3e303c..4da8c1a2c03 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/RemoveDomainPage.tsx @@ -66,7 +66,7 @@ export const RemoveDomainPage = () => { successMessage={localizationKeys('organizationProfile.removeDomainPage.successMessage', { domain: ref.current?.name, })} - deleteResource={() => domain?.delete().then(() => domains?.mutate?.())} + deleteResource={() => domain?.delete().then(() => domains?.revalidate?.())} breadcrumbTitle={localizationKeys('organizationProfile.profilePage.domainSection.title')} Breadcrumbs={OrganizationProfileBreadcrumbs} /> diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx index 0054046e7ce..e494b8f4a31 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx @@ -60,7 +60,7 @@ const RequestRow = withCardStateProvider( return card .runAsync(async () => { await request.accept(); - await membershipRequests.mutate(); + await membershipRequests.revalidate(); }, 'accept') .catch(err => handleError(err, [], onError)); }; @@ -71,7 +71,7 @@ const RequestRow = withCardStateProvider( return card .runAsync(async () => { await request.reject(); - await membershipRequests.mutate(); + await membershipRequests.revalidate(); }, 'reject') .catch(err => handleError(err, [], onError)); }; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx index f1f49029771..02be6245755 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx @@ -157,7 +157,7 @@ export const VerifiedDomainPage = withCardStateProvider(() => { deletePending: deletePending.checked, }); - await domains.mutate(); + await domains.revalidate(); await navigate('../../'); } catch (e) { diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx index 13d152a9518..fe2ba757e47 100644 --- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx @@ -44,13 +44,7 @@ const AcceptRejectSuggestionButtons = (props: OrganizationSuggestionResource) => const handleAccept = () => { return card .runAsync(props.accept) - .then(updatedItem => { - userSuggestions?.mutate?.(pages => populateCacheUpdateItem(updatedItem, pages), { - // Since `accept` gives back the updated information, - // we don't need to revalidate here. - revalidate: false, - }); - }) + .then(updatedItem => userSuggestions?.setCache?.(pages => populateCacheUpdateItem(updatedItem, pages))) .catch(err => handleError(err, [], card.setError)); }; @@ -87,13 +81,7 @@ const AcceptRejectInvitationButtons = (props: UserOrganizationInvitationResource const handleAccept = () => { return card .runAsync(props.accept) - .then(updatedItem => { - userInvitations?.mutate?.(pages => populateCacheRemoveItem(updatedItem, pages), { - // Since `accept` gives back the updated information, - // we don't need to revalidate here. - revalidate: false, - }); - }) + .then(updatedItem => userInvitations?.setCache?.(pages => populateCacheRemoveItem(updatedItem, pages))) .catch(err => handleError(err, [], card.setError)); }; From bf36da83d00c48f972de6af33ecf414e80e5e047 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Mon, 2 Oct 2023 16:03:00 +0300 Subject: [PATCH 06/10] chore(clerk-js,shared): Replace setCache with setData --- .../src/ui/components/OrganizationList/UserInvitationList.tsx | 2 +- .../src/ui/components/OrganizationList/UserSuggestionList.tsx | 2 +- .../OrganizationSwitcher/UserInvitationSuggestionList.tsx | 4 ++-- packages/shared/src/react/hooks/useOrganization.tsx | 2 +- packages/shared/src/react/hooks/useOrganizationList.tsx | 2 +- packages/shared/src/react/hooks/usePagesOrInfinite.ts | 4 ++-- packages/shared/src/react/types.ts | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx b/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx index dd2faa8f6cd..5fc91c5f526 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/UserInvitationList.tsx @@ -39,7 +39,7 @@ export const InvitationPreview = withCardStateProvider((props: UserOrganizationI }) .then(([updatedItem, organization]) => { // Update cache in case another listener depends on it - void userInvitations?.setCache?.(cachedPages => populateCacheUpdateItem(updatedItem, cachedPages)); + void userInvitations?.setData?.(cachedPages => populateCacheUpdateItem(updatedItem, cachedPages)); setAcceptedOrganization(organization); }) .catch(err => handleError(err, [], card.setError)); diff --git a/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx b/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx index 587d92e3e9e..3c8af375e7e 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/UserSuggestionList.tsx @@ -17,7 +17,7 @@ export const AcceptRejectInvitationButtons = (props: OrganizationSuggestionResou const handleAccept = () => { return card .runAsync(props.accept) - .then(updatedItem => userSuggestions?.setCache?.(pages => populateCacheUpdateItem(updatedItem, pages))) + .then(updatedItem => userSuggestions?.setData?.(pages => populateCacheUpdateItem(updatedItem, pages))) .catch(err => handleError(err, [], card.setError)); }; diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx index fe2ba757e47..847ccfea658 100644 --- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationSuggestionList.tsx @@ -44,7 +44,7 @@ const AcceptRejectSuggestionButtons = (props: OrganizationSuggestionResource) => const handleAccept = () => { return card .runAsync(props.accept) - .then(updatedItem => userSuggestions?.setCache?.(pages => populateCacheUpdateItem(updatedItem, pages))) + .then(updatedItem => userSuggestions?.setData?.(pages => populateCacheUpdateItem(updatedItem, pages))) .catch(err => handleError(err, [], card.setError)); }; @@ -81,7 +81,7 @@ const AcceptRejectInvitationButtons = (props: UserOrganizationInvitationResource const handleAccept = () => { return card .runAsync(props.accept) - .then(updatedItem => userInvitations?.setCache?.(pages => populateCacheRemoveItem(updatedItem, pages))) + .then(updatedItem => userInvitations?.setData?.(pages => populateCacheRemoveItem(updatedItem, pages))) .catch(err => handleError(err, [], card.setError)); }; diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx index 45b89e61dbe..be2c2036ba5 100644 --- a/packages/shared/src/react/hooks/useOrganization.tsx +++ b/packages/shared/src/react/hooks/useOrganization.tsx @@ -149,7 +149,7 @@ const undefinedPaginatedResource = { hasNextPage: false, hasPreviousPage: false, revalidate: undefined, - setCache: undefined, + setData: undefined, } as const; export const useOrganization: UseOrganization = params => { diff --git a/packages/shared/src/react/hooks/useOrganizationList.tsx b/packages/shared/src/react/hooks/useOrganizationList.tsx index 3a07dd96118..258197713b5 100644 --- a/packages/shared/src/react/hooks/useOrganizationList.tsx +++ b/packages/shared/src/react/hooks/useOrganizationList.tsx @@ -52,7 +52,7 @@ const undefinedPaginatedResource = { hasNextPage: false, hasPreviousPage: false, revalidate: undefined, - setCache: undefined, + setData: undefined, } as const; type UseOrganizationList = ( diff --git a/packages/shared/src/react/hooks/usePagesOrInfinite.ts b/packages/shared/src/react/hooks/usePagesOrInfinite.ts index 134765dea9d..fef2d0d3739 100644 --- a/packages/shared/src/react/hooks/usePagesOrInfinite.ts +++ b/packages/shared/src/react/hooks/usePagesOrInfinite.ts @@ -206,7 +206,7 @@ export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, const hasNextPage = count - offsetCount * pageSizeRef.current > page * pageSizeRef.current; const hasPreviousPage = (page - 1) * pageSizeRef.current > offsetCount * pageSizeRef.current; - const setCache: CacheSetter = triggerInfinite + const setData: CacheSetter = triggerInfinite ? value => swrInfiniteMutate(value, { revalidate: false, @@ -234,6 +234,6 @@ export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, // Let the hook return type define this type revalidate: revalidate as any, // Let the hook return type define this type - setCache: setCache as any, + setData: setData as any, }; }; diff --git a/packages/shared/src/react/types.ts b/packages/shared/src/react/types.ts index a428df867bd..737b4a19985 100644 --- a/packages/shared/src/react/types.ts +++ b/packages/shared/src/react/types.ts @@ -18,7 +18,7 @@ export type PaginatedResources Promise; - setCache: Infinite extends true + setData: Infinite extends true ? // Array of pages of data CacheSetter<(ArrayOrPaginated | undefined)[]> : // Array of data From 67d3c1fef183c4ca7589593de6466a3992619144 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Thu, 12 Oct 2023 16:29:57 +0300 Subject: [PATCH 07/10] fix(shared): Remove unnecessary complexity in types --- .../src/react/hooks/useOrganization.tsx | 31 +++++-------------- .../src/react/hooks/useOrganizationList.tsx | 24 ++++---------- .../src/react/hooks/usePagesOrInfinite.ts | 2 +- packages/shared/src/react/types.ts | 8 +++-- 4 files changed, 20 insertions(+), 45 deletions(-) diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx index be2c2036ba5..a104d240d6b 100644 --- a/packages/shared/src/react/hooks/useOrganization.tsx +++ b/packages/shared/src/react/hooks/useOrganization.tsx @@ -107,31 +107,19 @@ type UseOrganization = ( membership: OrganizationMembershipResource | null | undefined; domains: PaginatedResources< OrganizationDomainResource, - T['membershipRequests'] extends { infinite: true } ? true : false, - T['membershipRequests'] extends { infinite: true } - ? ClerkPaginatedResponse - : OrganizationDomainResource[] + T['membershipRequests'] extends { infinite: true } ? true : false > | null; membershipRequests: PaginatedResources< OrganizationMembershipRequestResource, - T['membershipRequests'] extends { infinite: true } ? true : false, - T['membershipRequests'] extends { infinite: true } - ? ClerkPaginatedResponse - : OrganizationMembershipRequestResource[] + T['membershipRequests'] extends { infinite: true } ? true : false > | null; memberships: PaginatedResources< OrganizationMembershipResource, - T['memberships'] extends { infinite: true } ? true : false, - T['memberships'] extends { infinite: true } - ? ClerkPaginatedResponse - : OrganizationMembershipResource[] + T['memberships'] extends { infinite: true } ? true : false > | null; invitations: PaginatedResources< OrganizationInvitationResource, - T['invitations'] extends { infinite: true } ? true : false, - T['invitations'] extends { infinite: true } - ? ClerkPaginatedResponse - : OrganizationInvitationResource[] + T['invitations'] extends { infinite: true } ? true : false > | null; }; @@ -397,13 +385,10 @@ export const useOrganization: UseOrganization = params => { void mutateMembershipList(); void mutateInvitationList(); }, - // Let the hook return type define this type - domains: domains as any, - // Let the hook return type define this type - membershipRequests: membershipRequests as any, - // Let the hook return type define this type - memberships: memberships as any, - invitations: invitations as any, + domains, + membershipRequests, + memberships, + invitations, }; }; diff --git a/packages/shared/src/react/hooks/useOrganizationList.tsx b/packages/shared/src/react/hooks/useOrganizationList.tsx index 258197713b5..606feb4934d 100644 --- a/packages/shared/src/react/hooks/useOrganizationList.tsx +++ b/packages/shared/src/react/hooks/useOrganizationList.tsx @@ -80,24 +80,15 @@ type UseOrganizationList = ( setActive: SetActive; userMemberships: PaginatedResources< OrganizationMembershipResource, - T['userMemberships'] extends { infinite: true } ? true : false, - T['userMemberships'] extends { infinite: true } - ? ClerkPaginatedResponse - : OrganizationMembershipResource[] + T['userMemberships'] extends { infinite: true } ? true : false >; userInvitations: PaginatedResources< UserOrganizationInvitationResource, - T['userInvitations'] extends { infinite: true } ? true : false, - T['userInvitations'] extends { infinite: true } - ? ClerkPaginatedResponse - : UserOrganizationInvitationResource[] + T['userInvitations'] extends { infinite: true } ? true : false >; userSuggestions: PaginatedResources< OrganizationSuggestionResource, - T['userSuggestions'] extends { infinite: true } ? true : false, - T['userSuggestions'] extends { infinite: true } - ? ClerkPaginatedResponse - : OrganizationSuggestionResource[] + T['userSuggestions'] extends { infinite: true } ? true : false >; }; @@ -234,12 +225,9 @@ export const useOrganizationList: UseOrganizationList = params => { organizationList: createOrganizationList(user.organizationMemberships), setActive: clerk.setActive, createOrganization: clerk.createOrganization, - // Let the hook return type define this type - userMemberships: memberships as any, - // Let the hook return type define this type - userInvitations: invitations as any, - // Let the hook return type define this type - userSuggestions: suggestions as any, + userMemberships: memberships, + userInvitations: invitations, + userSuggestions: suggestions, }; deprecatedObjectProperty(result, 'organizationList', 'Use `userMemberships` instead.'); diff --git a/packages/shared/src/react/hooks/usePagesOrInfinite.ts b/packages/shared/src/react/hooks/usePagesOrInfinite.ts index fef2d0d3739..5dc37c80939 100644 --- a/packages/shared/src/react/hooks/usePagesOrInfinite.ts +++ b/packages/shared/src/react/hooks/usePagesOrInfinite.ts @@ -88,7 +88,7 @@ type UsePagesOrInfinite = < */ options: TOptions, cacheKeys: CacheKeys, -) => PaginatedResources, TOptions['infinite'], FetcherReturnData>; +) => PaginatedResources, TOptions['infinite']>; export const usePagesOrInfinite: UsePagesOrInfinite = (params, fetcher, options, cacheKeys) => { const [paginatedPage, setPaginatedPage] = useState(params.initialPage ?? 1); diff --git a/packages/shared/src/react/types.ts b/packages/shared/src/react/types.ts index 737b4a19985..7a8abff1287 100644 --- a/packages/shared/src/react/types.ts +++ b/packages/shared/src/react/types.ts @@ -1,10 +1,12 @@ +import type { ClerkPaginatedResponse } from '@clerk/types'; + export type ValueOrSetter = (size: T | ((_size: T) => T)) => void; export type CacheSetter = ( data?: CData | ((currentData?: CData) => Promise | undefined | CData), ) => Promise; -export type PaginatedResources = { +export type PaginatedResources = { data: T[]; count: number; isLoading: boolean; @@ -20,9 +22,9 @@ export type PaginatedResources Promise; setData: Infinite extends true ? // Array of pages of data - CacheSetter<(ArrayOrPaginated | undefined)[]> + CacheSetter<(ClerkPaginatedResponse | undefined)[]> : // Array of data - CacheSetter; + CacheSetter | undefined>; }; // Utility type to convert PaginatedDataAPI to properties as undefined, except booleans set to false From 31645dc903ff298a3a50fb764f162bee11841c52 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 18 Oct 2023 14:18:30 +0300 Subject: [PATCH 08/10] chore(clerk-js): Update after rebase --- .../ui/components/OrganizationProfile/ActiveMembersList.tsx | 2 +- packages/shared/src/react/hooks/usePagesOrInfinite.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx index 6a23ac4b640..141a49b99b8 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx @@ -9,7 +9,7 @@ import { DataTable, RoleSelect, RowContainer } from './MemberListTable'; export const ActiveMembersList = () => { const card = useCardState(); - const { organization, memberships, ...rest } = useCoreOrganization({ + const { organization, memberships } = useCoreOrganization({ memberships: true, }); diff --git a/packages/shared/src/react/hooks/usePagesOrInfinite.ts b/packages/shared/src/react/hooks/usePagesOrInfinite.ts index 5dc37c80939..6d51342fce2 100644 --- a/packages/shared/src/react/hooks/usePagesOrInfinite.ts +++ b/packages/shared/src/react/hooks/usePagesOrInfinite.ts @@ -2,8 +2,8 @@ import { useCallback, useMemo, useRef, useState } from 'react'; -import { useSWR, useSWRInfinite } from './clerk-swr'; -import type { CacheSetter, PaginatedResources, ValueOrSetter } from './types'; +import { useSWR, useSWRInfinite } from '../clerk-swr'; +import type { CacheSetter, PaginatedResources, ValueOrSetter } from '../types'; function getDifferentKeys(obj1: Record, obj2: Record): Record { const keysSet = new Set(Object.keys(obj2)); From 06a7d7ddc0524f98ed19c0d88366338d455af0c7 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 1 Nov 2023 12:42:51 +0200 Subject: [PATCH 09/10] chore(shared): Add comments with context --- .../src/ui/components/OrganizationSwitcher/utils.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts b/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts index 7f49d4ee0b6..38c62349587 100644 --- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts +++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/utils.ts @@ -23,6 +23,10 @@ export const populateCacheUpdateItem = ( return [{ data: [updatedItem], total_count: 1 }]; } + /** + * We should "preserve" an undefined page if one is found. For example if swr triggers 2 requests, page 1 & page2, and the request for page2 resolves first, at that point in memory itemsInfinitePages would look like this [undefined, {....}] + * if SWR says that has fetched 2 pages but the first result of is undefined, we should not return back an array with 1 item as this will end up having cacheKeys that point nowhere. + */ return itemsInfinitePages.map(item => { if (typeof item === 'undefined') { return item; @@ -50,6 +54,10 @@ export const populateCacheRemoveItem = ( return undefined; } + /** + * We should "preserve" an undefined page if one is found. For example if swr triggers 2 requests, page 1 & page2, and the request for page2 resolves first, at that point in memory itemsInfinitePages would look like this [undefined, {....}] + * if SWR says that has fetched 2 pages but the first result of is undefined, we should not return back an array with 1 item as this will end up having cacheKeys that point nowhere. + */ return itemsInfinitePages?.map(item => { if (typeof item === 'undefined') { return item; From d760fbc617b8c756758af65e96bb7e0868e1885b Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 1 Nov 2023 12:44:04 +0200 Subject: [PATCH 10/10] chore(shared): Update changeset --- .changeset/blue-ghosts-float.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/blue-ghosts-float.md b/.changeset/blue-ghosts-float.md index 19edafa659f..de8929d4532 100644 --- a/.changeset/blue-ghosts-float.md +++ b/.changeset/blue-ghosts-float.md @@ -1,6 +1,6 @@ --- -'@clerk/shared': patch +'@clerk/shared': minor --- -Expose mutate for paginated lists of data in organization hooks. -`const {userMemberships:{mutate}} = useOrganizationList({userMemberships:true})` +Expose `revalidate` and `setData` for paginated lists of data in organization hooks. +`const {userMemberships:{revalidate, setData}} = useOrganizationList({userMemberships:true})`