From 4a9c06a096311111ec64464656666baffb134461 Mon Sep 17 00:00:00 2001 From: subramanyachakravarthy-okta Date: Mon, 25 May 2026 11:58:22 +0530 Subject: [PATCH 1/4] fix(react): support react-query v4/v5 keeppreviousdata compat --- .../shared/services/use-member-management-service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts b/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts index 7c8d17e6..fd32d3ed 100644 --- a/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts +++ b/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts @@ -25,6 +25,11 @@ import type { MemberManagementServiceResult, } from '@/types/my-organization/member-management/organization-member-management-types'; +const keepPreviousDataOption = + typeof keepPreviousData === 'function' + ? { placeholderData: keepPreviousData } + : ({ keepPreviousData: true } as object); + const INVITATION_SORT_FIELD_MAP: Record = { created_at: 'created_at', }; @@ -115,7 +120,7 @@ export function useMemberManagementService( return { invitations, next }; }, enabled: !!coreClient && isInvitationsTabActive, - placeholderData: keepPreviousData, + ...keepPreviousDataOption, }); const createInvitationMutation = useMutation({ From 81ea5a9aa348bb20dbb7ea0bf66c737d586748fd Mon Sep 17 00:00:00 2001 From: subramanyachakravarthy-okta Date: Mon, 25 May 2026 12:21:18 +0530 Subject: [PATCH 2/4] feat(react): tanstack v4 support --- .../src/hooks/my-organization/use-member-detail.ts | 11 +++++++---- .../use-organization-member-management.ts | 9 ++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/react/src/hooks/my-organization/use-member-detail.ts b/packages/react/src/hooks/my-organization/use-member-detail.ts index 4c1aed7f..8ca94b4d 100644 --- a/packages/react/src/hooks/my-organization/use-member-detail.ts +++ b/packages/react/src/hooks/my-organization/use-member-detail.ts @@ -124,10 +124,13 @@ export function useOrganizationMemberDetail( isFetchingMemberRoles: memberRolesQuery.isLoading, isFetchingAvailableRoles: rolesQuery.isLoading || rolesQuery.isFetching, isLoading: memberQuery.isLoading, - isRemovingFromOrg: removeFromOrgMutation.isPending, - isAssigningRoles: assignRolesMutation.isPending, - isRemovingRoles: removeRolesMutation.isPending, - removingRoleIds: removeRolesMutation.isPending ? removingRoles.map((r) => r.id) : [], + isRemovingFromOrg: removeFromOrgMutation.isPending ?? (removeFromOrgMutation as any).isLoading, + isAssigningRoles: assignRolesMutation.isPending ?? (assignRolesMutation as any).isLoading, + isRemovingRoles: removeRolesMutation.isPending ?? (removeRolesMutation as any).isLoading, + removingRoleIds: + (removeRolesMutation.isPending ?? (removeRolesMutation as any).isLoading) + ? removingRoles.map((r) => r.id) + : [], modalState, setActiveTab, diff --git a/packages/react/src/hooks/my-organization/use-organization-member-management.ts b/packages/react/src/hooks/my-organization/use-organization-member-management.ts index 53c4f6bc..60c31893 100644 --- a/packages/react/src/hooks/my-organization/use-organization-member-management.ts +++ b/packages/react/src/hooks/my-organization/use-organization-member-management.ts @@ -181,9 +181,12 @@ export function useOrganizationMemberManagement( invitations: currentInvitations, isInitialLoading: invitationsQuery.isLoading, isFetchingInvitations: invitationsQuery.isFetching, - isCreatingInvitation: createInvitationMutation.isPending, - isRevokingInvitation: revokeInvitationMutation.isPending, - isResendingInvitation: resendInvitationMutation.isPending, + isCreatingInvitation: + createInvitationMutation.isPending ?? (createInvitationMutation as any).isLoading, + isRevokingInvitation: + revokeInvitationMutation.isPending ?? (revokeInvitationMutation as any).isLoading, + isResendingInvitation: + resendInvitationMutation.isPending ?? (resendInvitationMutation as any).isLoading, invitationPagination: { pageSize: invitationPageSize, currentPage: invitationCurrentPage, From 0dc44bac3c6694e15df6906fd8ef9ca3cd7fb002 Mon Sep 17 00:00:00 2001 From: subramanyachakravarthy-okta Date: Wed, 27 May 2026 00:59:03 +0530 Subject: [PATCH 3/4] feat(react): add tanstack v4/v5 compat layer for member management - add isMutationPending and getKeepPreviousDataOption helpers - update member management hooks to use compat utilities - remove as any casts and direct keepPreviousData usage --- .../services/use-member-management-service.ts | 10 ++--- .../my-organization/use-member-detail.ts | 12 +++--- .../use-organization-member-management.ts | 14 +++---- .../react/src/lib/utils/tanstack-compat.ts | 38 +++++++++++++++++++ 4 files changed, 53 insertions(+), 21 deletions(-) create mode 100644 packages/react/src/lib/utils/tanstack-compat.ts diff --git a/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts b/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts index 14decf5f..71b5cdd9 100644 --- a/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts +++ b/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts @@ -11,13 +11,14 @@ import { memberManagementQueryKeys, OrganizationDetailsMappers, } from '@auth0/universal-components-core'; -import { useQuery, useMutation, useQueryClient, keepPreviousData } from '@tanstack/react-query'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import React from 'react'; import { showToast } from '@/components/auth0/shared/toast'; import { useCoreClient } from '@/hooks/shared/use-core-client'; import { useErrorHandler } from '@/hooks/shared/use-error-handler'; import { useTranslator } from '@/hooks/shared/use-translator'; +import { getKeepPreviousDataOption } from '@/lib/utils/tanstack-compat'; import type { CreateInvitationInput } from '@/types/my-organization/member-management/organization-invitation-table-types'; import type { UseMemberManagementServiceOptions, @@ -25,10 +26,7 @@ import type { MemberManagementSortConfig, } from '@/types/my-organization/member-management/organization-member-management-types'; -const keepPreviousDataOption = - typeof keepPreviousData === 'function' - ? { placeholderData: keepPreviousData } - : ({ keepPreviousData: true } as object); +const keepPreviousDataOption = getKeepPreviousDataOption(); const INVITATION_SORT_FIELD_MAP: Record = { created_at: 'created_at', @@ -142,7 +140,7 @@ export function useMemberManagementService( return { members, next }; }, enabled: !!coreClient && !isInvitationsTabActive && !!memberParams, - placeholderData: keepPreviousData, + ...keepPreviousDataOption, }); const organizationQuery = useQuery({ diff --git a/packages/react/src/hooks/my-organization/use-member-detail.ts b/packages/react/src/hooks/my-organization/use-member-detail.ts index 8ca94b4d..b215f4c2 100644 --- a/packages/react/src/hooks/my-organization/use-member-detail.ts +++ b/packages/react/src/hooks/my-organization/use-member-detail.ts @@ -7,6 +7,7 @@ import type { Role } from '@auth0/universal-components-core'; import * as React from 'react'; import { useMemberDetailService } from '@/hooks/my-organization/shared/services/use-member-detail-service'; +import { isMutationPending } from '@/lib/utils/tanstack-compat'; import type { MemberDetailModalState, MemberDetailTab, @@ -124,13 +125,10 @@ export function useOrganizationMemberDetail( isFetchingMemberRoles: memberRolesQuery.isLoading, isFetchingAvailableRoles: rolesQuery.isLoading || rolesQuery.isFetching, isLoading: memberQuery.isLoading, - isRemovingFromOrg: removeFromOrgMutation.isPending ?? (removeFromOrgMutation as any).isLoading, - isAssigningRoles: assignRolesMutation.isPending ?? (assignRolesMutation as any).isLoading, - isRemovingRoles: removeRolesMutation.isPending ?? (removeRolesMutation as any).isLoading, - removingRoleIds: - (removeRolesMutation.isPending ?? (removeRolesMutation as any).isLoading) - ? removingRoles.map((r) => r.id) - : [], + isRemovingFromOrg: isMutationPending(removeFromOrgMutation), + isAssigningRoles: isMutationPending(assignRolesMutation), + isRemovingRoles: isMutationPending(removeRolesMutation), + removingRoleIds: isMutationPending(removeRolesMutation) ? removingRoles.map((r) => r.id) : [], modalState, setActiveTab, diff --git a/packages/react/src/hooks/my-organization/use-organization-member-management.ts b/packages/react/src/hooks/my-organization/use-organization-member-management.ts index d4c754c7..90c8ffa9 100644 --- a/packages/react/src/hooks/my-organization/use-organization-member-management.ts +++ b/packages/react/src/hooks/my-organization/use-organization-member-management.ts @@ -10,6 +10,7 @@ import { showToast } from '@/components/auth0/shared/toast'; import { useMemberManagementService } from '@/hooks/my-organization/shared/services/use-member-management-service'; import { useCheckpointPagination } from '@/hooks/shared/use-checkpoint-pagination'; import { useTranslator } from '@/hooks/shared/use-translator'; +import { isMutationPending } from '@/lib/utils/tanstack-compat'; import type { CreateInvitationInput, IdentityProviderOption, @@ -270,16 +271,13 @@ export function useOrganizationMemberManagement( orgDisplayName: orgDisplayName, isInitialLoading: invitationsQuery.isLoading || membersQuery.isLoading, isFetchingInvitations: invitationsQuery.isFetching, - isCreatingInvitation: - createInvitationMutation.isPending ?? (createInvitationMutation as any).isLoading, - isRevokingInvitation: - revokeInvitationMutation.isPending ?? (revokeInvitationMutation as any).isLoading, - isResendingInvitation: - resendInvitationMutation.isPending ?? (resendInvitationMutation as any).isLoading, + isCreatingInvitation: isMutationPending(createInvitationMutation), + isRevokingInvitation: isMutationPending(revokeInvitationMutation), + isResendingInvitation: isMutationPending(resendInvitationMutation), isFetchingMembers: membersQuery.isFetching, isFetchingAvailableRoles: rolesQuery.isLoading || rolesQuery.isFetching, - isRemovingFromOrg: removeFromOrgMutation.isPending ?? (removeFromOrgMutation as any).isLoading, - isAssigningRoles: assignRolesMutation.isPending ?? (assignRolesMutation as any).isLoading, + isRemovingFromOrg: isMutationPending(removeFromOrgMutation), + isAssigningRoles: isMutationPending(assignRolesMutation), invitationPagination: { pageSize: invitationPageSize, currentPage: invitationCurrentPage, diff --git a/packages/react/src/lib/utils/tanstack-compat.ts b/packages/react/src/lib/utils/tanstack-compat.ts new file mode 100644 index 00000000..960b115f --- /dev/null +++ b/packages/react/src/lib/utils/tanstack-compat.ts @@ -0,0 +1,38 @@ +/** + * TanStack Query v4/v5 compatibility utilities. + * @module tanstack-compat + * @internal + */ + +import type { UseMutationResult } from '@tanstack/react-query'; +import { keepPreviousData } from '@tanstack/react-query'; + +type MutationV4Compat = { isLoading?: boolean }; + +/** + * Returns whether a mutation is in a pending/loading state, compatible with both v4 and v5. + * In v5, `isPending` replaces `isLoading` on mutations. + * @param mutation - The mutation result object. + * @returns `true` if the mutation is pending or loading. + */ +export function isMutationPending< + T extends Pick & MutationV4Compat, +>(mutation: T): boolean { + return mutation.isPending ?? mutation.isLoading ?? false; +} + +type KeepPreviousDataOptionV5 = { placeholderData: typeof keepPreviousData }; +type KeepPreviousDataOptionV4 = { keepPreviousData: true }; +type KeepPreviousDataOption = KeepPreviousDataOptionV5 | KeepPreviousDataOptionV4; + +/** + * Returns the correct query option for keeping previous data, compatible with both v4 and v5. + * In v5, `keepPreviousData` boolean option was replaced by `placeholderData: keepPreviousData`. + * @returns The appropriate option object to spread into a query config. + */ +export function getKeepPreviousDataOption(): KeepPreviousDataOption { + if (typeof keepPreviousData === 'function') { + return { placeholderData: keepPreviousData }; + } + return { keepPreviousData: true }; +} From b8ee35ec176a2bd5e8150effa86f80fb3bcc5b93 Mon Sep 17 00:00:00 2001 From: subramanyachakravarthy-okta Date: Fri, 29 May 2026 19:13:12 +0530 Subject: [PATCH 4/4] refactor(react): rename mutation state utility functions --- .../services/use-member-management-service.ts | 4 ++-- .../src/hooks/my-organization/use-member-detail.ts | 10 +++++----- .../use-organization-member-management.ts | 12 ++++++------ packages/react/src/lib/utils/tanstack-compat.ts | 14 ++++++-------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts b/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts index 71b5cdd9..87f4a75f 100644 --- a/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts +++ b/packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts @@ -18,7 +18,7 @@ import { showToast } from '@/components/auth0/shared/toast'; import { useCoreClient } from '@/hooks/shared/use-core-client'; import { useErrorHandler } from '@/hooks/shared/use-error-handler'; import { useTranslator } from '@/hooks/shared/use-translator'; -import { getKeepPreviousDataOption } from '@/lib/utils/tanstack-compat'; +import { getPreviousDataOption } from '@/lib/utils/tanstack-compat'; import type { CreateInvitationInput } from '@/types/my-organization/member-management/organization-invitation-table-types'; import type { UseMemberManagementServiceOptions, @@ -26,7 +26,7 @@ import type { MemberManagementSortConfig, } from '@/types/my-organization/member-management/organization-member-management-types'; -const keepPreviousDataOption = getKeepPreviousDataOption(); +const keepPreviousDataOption = getPreviousDataOption(); const INVITATION_SORT_FIELD_MAP: Record = { created_at: 'created_at', diff --git a/packages/react/src/hooks/my-organization/use-member-detail.ts b/packages/react/src/hooks/my-organization/use-member-detail.ts index b215f4c2..7f4d4ad9 100644 --- a/packages/react/src/hooks/my-organization/use-member-detail.ts +++ b/packages/react/src/hooks/my-organization/use-member-detail.ts @@ -7,7 +7,7 @@ import type { Role } from '@auth0/universal-components-core'; import * as React from 'react'; import { useMemberDetailService } from '@/hooks/my-organization/shared/services/use-member-detail-service'; -import { isMutationPending } from '@/lib/utils/tanstack-compat'; +import { isMutationLoading } from '@/lib/utils/tanstack-compat'; import type { MemberDetailModalState, MemberDetailTab, @@ -125,10 +125,10 @@ export function useOrganizationMemberDetail( isFetchingMemberRoles: memberRolesQuery.isLoading, isFetchingAvailableRoles: rolesQuery.isLoading || rolesQuery.isFetching, isLoading: memberQuery.isLoading, - isRemovingFromOrg: isMutationPending(removeFromOrgMutation), - isAssigningRoles: isMutationPending(assignRolesMutation), - isRemovingRoles: isMutationPending(removeRolesMutation), - removingRoleIds: isMutationPending(removeRolesMutation) ? removingRoles.map((r) => r.id) : [], + isRemovingFromOrg: isMutationLoading(removeFromOrgMutation), + isAssigningRoles: isMutationLoading(assignRolesMutation), + isRemovingRoles: isMutationLoading(removeRolesMutation), + removingRoleIds: isMutationLoading(removeRolesMutation) ? removingRoles.map((r) => r.id) : [], modalState, setActiveTab, diff --git a/packages/react/src/hooks/my-organization/use-organization-member-management.ts b/packages/react/src/hooks/my-organization/use-organization-member-management.ts index 90c8ffa9..256023d5 100644 --- a/packages/react/src/hooks/my-organization/use-organization-member-management.ts +++ b/packages/react/src/hooks/my-organization/use-organization-member-management.ts @@ -10,7 +10,7 @@ import { showToast } from '@/components/auth0/shared/toast'; import { useMemberManagementService } from '@/hooks/my-organization/shared/services/use-member-management-service'; import { useCheckpointPagination } from '@/hooks/shared/use-checkpoint-pagination'; import { useTranslator } from '@/hooks/shared/use-translator'; -import { isMutationPending } from '@/lib/utils/tanstack-compat'; +import { isMutationLoading } from '@/lib/utils/tanstack-compat'; import type { CreateInvitationInput, IdentityProviderOption, @@ -271,13 +271,13 @@ export function useOrganizationMemberManagement( orgDisplayName: orgDisplayName, isInitialLoading: invitationsQuery.isLoading || membersQuery.isLoading, isFetchingInvitations: invitationsQuery.isFetching, - isCreatingInvitation: isMutationPending(createInvitationMutation), - isRevokingInvitation: isMutationPending(revokeInvitationMutation), - isResendingInvitation: isMutationPending(resendInvitationMutation), + isCreatingInvitation: isMutationLoading(createInvitationMutation), + isRevokingInvitation: isMutationLoading(revokeInvitationMutation), + isResendingInvitation: isMutationLoading(resendInvitationMutation), isFetchingMembers: membersQuery.isFetching, isFetchingAvailableRoles: rolesQuery.isLoading || rolesQuery.isFetching, - isRemovingFromOrg: isMutationPending(removeFromOrgMutation), - isAssigningRoles: isMutationPending(assignRolesMutation), + isRemovingFromOrg: isMutationLoading(removeFromOrgMutation), + isAssigningRoles: isMutationLoading(assignRolesMutation), invitationPagination: { pageSize: invitationPageSize, currentPage: invitationCurrentPage, diff --git a/packages/react/src/lib/utils/tanstack-compat.ts b/packages/react/src/lib/utils/tanstack-compat.ts index 960b115f..b06b2fc7 100644 --- a/packages/react/src/lib/utils/tanstack-compat.ts +++ b/packages/react/src/lib/utils/tanstack-compat.ts @@ -7,30 +7,28 @@ import type { UseMutationResult } from '@tanstack/react-query'; import { keepPreviousData } from '@tanstack/react-query'; -type MutationV4Compat = { isLoading?: boolean }; - /** * Returns whether a mutation is in a pending/loading state, compatible with both v4 and v5. * In v5, `isPending` replaces `isLoading` on mutations. * @param mutation - The mutation result object. * @returns `true` if the mutation is pending or loading. */ -export function isMutationPending< - T extends Pick & MutationV4Compat, +export function isMutationLoading< + T extends Pick & { isLoading?: boolean }, >(mutation: T): boolean { return mutation.isPending ?? mutation.isLoading ?? false; } -type KeepPreviousDataOptionV5 = { placeholderData: typeof keepPreviousData }; -type KeepPreviousDataOptionV4 = { keepPreviousData: true }; -type KeepPreviousDataOption = KeepPreviousDataOptionV5 | KeepPreviousDataOptionV4; +type PlaceholderDataOption = { placeholderData: typeof keepPreviousData }; +type LegacyKeepPreviousOption = { keepPreviousData: true }; +type PreviousDataOption = PlaceholderDataOption | LegacyKeepPreviousOption; /** * Returns the correct query option for keeping previous data, compatible with both v4 and v5. * In v5, `keepPreviousData` boolean option was replaced by `placeholderData: keepPreviousData`. * @returns The appropriate option object to spread into a query config. */ -export function getKeepPreviousDataOption(): KeepPreviousDataOption { +export function getPreviousDataOption(): PreviousDataOption { if (typeof keepPreviousData === 'function') { return { placeholderData: keepPreviousData }; }