From b95b3a60ad4464bb2a729327b8b3f2d78cd9ea95 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:10:08 -0700 Subject: [PATCH] chore(deps): update dependency @tanstack/react-query to v5 (#2404) * fix(deps): update dependency @tanstack/react-query to v5 * chore: update for compatibility with React Query v5 * chore: update for compatibility with React Query v5 * test: update tests --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Braden MacDonald --- package-lock.json | 29 +++------- package.json | 2 +- .../ContentTagsCollapsible.jsx | 4 +- .../ContentTagsDrawer.test.jsx | 18 +++--- .../data/{apiHooks.jsx => apiHooks.ts} | 58 ++++++++----------- src/course-libraries/OutOfSyncAlert.tsx | 6 +- src/course-libraries/ReviewTabContent.tsx | 2 +- src/data/apiHooks.ts | 2 +- src/generic/clipboard/hooks/useClipboard.ts | 2 +- .../add-content/AddContent.tsx | 2 +- .../collections/LibraryCollectionPage.tsx | 4 +- .../component-info/ComponentAdvancedInfo.tsx | 4 +- .../component-info/ComponentDetails.tsx | 4 +- .../component-picker/ComponentPicker.test.tsx | 35 ++++++++--- .../component-picker/SelectLibrary.tsx | 4 +- .../create-library/CreateLibrary.tsx | 4 +- src/library-authoring/data/apiHooks.ts | 7 ++- .../hierarchy/ItemHierarchy.tsx | 4 +- .../hierarchy/ItemHierarchyPublisher.tsx | 4 +- src/search-manager/data/apiHooks.test.tsx | 2 +- src/search-manager/data/apiHooks.ts | 15 ++--- .../manage-orgs/data/{api.js => api.ts} | 20 +------ src/taxonomy/tag-list/TagListTable.jsx | 2 +- 23 files changed, 110 insertions(+), 124 deletions(-) rename src/content-tags-drawer/data/{apiHooks.jsx => apiHooks.ts} (75%) rename src/taxonomy/manage-orgs/data/{api.js => api.ts} (71%) diff --git a/package-lock.json b/package-lock.json index 59c1eec68..2cc1cc03e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "@openedx/paragon": "^23.5.0", "@redux-devtools/extension": "^3.3.0", "@reduxjs/toolkit": "1.9.7", - "@tanstack/react-query": "4.40.1", + "@tanstack/react-query": "5.89.0", "@tinymce/tinymce-react": "^3.14.0", "classnames": "2.5.1", "codemirror": "^6.0.0", @@ -5889,9 +5889,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.40.0.tgz", - "integrity": "sha512-7MJTtZkCSuehMC7IxMOCGsLvHS3jHx4WjveSrGsG1Nc1UQLjaFwwkpLA2LmPfvOAxnH4mszMOBFD6LlZE+aB+Q==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.89.0.tgz", + "integrity": "sha512-joFV1MuPhSLsKfTzwjmPDrp8ENfZ9N23ymFu07nLfn3JCkSHy0CFgsyhHTJOmWaumC/WiNIKM0EJyduCF/Ih/Q==", "license": "MIT", "funding": { "type": "github", @@ -5899,30 +5899,19 @@ } }, "node_modules/@tanstack/react-query": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.40.1.tgz", - "integrity": "sha512-mgD07S5N8e5v81CArKDWrHE4LM7HxZ9k/KLeD3+NUD9WimGZgKIqojUZf/rXkfAMYZU9p0Chzj2jOXm7xpgHHQ==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.89.0.tgz", + "integrity": "sha512-SXbtWSTSRXyBOe80mszPxpEbaN4XPRUp/i0EfQK1uyj3KCk/c8FuPJNIRwzOVe/OU3rzxrYtiNabsAmk1l714A==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "4.40.0", - "use-sync-external-store": "^1.2.0" + "@tanstack/query-core": "5.89.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-native": "*" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } + "react": "^18 || ^19" } }, "node_modules/@testing-library/dom": { diff --git a/package.json b/package.json index 1faf5cdbd..6a9d317a2 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@openedx/paragon": "^23.5.0", "@redux-devtools/extension": "^3.3.0", "@reduxjs/toolkit": "1.9.7", - "@tanstack/react-query": "4.40.1", + "@tanstack/react-query": "5.89.0", "@tinymce/tinymce-react": "^3.14.0", "classnames": "2.5.1", "codemirror": "^6.0.0", diff --git a/src/content-tags-drawer/ContentTagsCollapsible.jsx b/src/content-tags-drawer/ContentTagsCollapsible.jsx index 47399f8d4..c82852869 100644 --- a/src/content-tags-drawer/ContentTagsCollapsible.jsx +++ b/src/content-tags-drawer/ContentTagsCollapsible.jsx @@ -435,8 +435,8 @@ const ContentTagsCollapsible = ({ onKeyDown={handleSelectOnKeyDown} ref={/** @type {React.RefObject} */(selectRef)} isMulti - isLoading={updateTags.isLoading} - isDisabled={updateTags.isLoading} + isLoading={updateTags.isPending} + isDisabled={updateTags.isPending} name="tags-select" placeholder={intl.formatMessage(messages.collapsibleAddTagsPlaceholderText)} isSearchable diff --git a/src/content-tags-drawer/ContentTagsDrawer.test.jsx b/src/content-tags-drawer/ContentTagsDrawer.test.jsx index 90fb14568..2d708509a 100644 --- a/src/content-tags-drawer/ContentTagsDrawer.test.jsx +++ b/src/content-tags-drawer/ContentTagsDrawer.test.jsx @@ -719,14 +719,16 @@ describe('', () => { await waitFor(() => expect(axiosMock.history.put[0].url).toEqual(url)); expect(mockInvalidateQueries).toHaveBeenCalledTimes(5); - expect(mockInvalidateQueries).toHaveBeenNthCalledWith(5, [ - 'contentLibrary', - 'lib:org:lib', - 'content', - 'container', - containerId, - 'children', - ]); + expect(mockInvalidateQueries).toHaveBeenNthCalledWith(5, { + queryKey: [ + 'contentLibrary', + 'lib:org:lib', + 'content', + 'container', + containerId, + 'children', + ], + }); }); }); diff --git a/src/content-tags-drawer/data/apiHooks.jsx b/src/content-tags-drawer/data/apiHooks.ts similarity index 75% rename from src/content-tags-drawer/data/apiHooks.jsx rename to src/content-tags-drawer/data/apiHooks.ts index fcd8da92b..f04102f7b 100644 --- a/src/content-tags-drawer/data/apiHooks.jsx +++ b/src/content-tags-drawer/data/apiHooks.ts @@ -1,4 +1,3 @@ -// @ts-check import { useMemo } from 'react'; import { getConfig } from '@edx/frontend-platform'; import { @@ -8,6 +7,7 @@ import { useQueryClient, } from '@tanstack/react-query'; import { useParams } from 'react-router'; +import { TagData, TagListData } from '@src/taxonomy/data/types'; import { getTaxonomyTagsData, getContentTaxonomyTagsData, @@ -17,18 +17,16 @@ import { } from './api'; import { libraryAuthoringQueryKeys, libraryQueryPredicate, xblockQueryKeys } from '../../library-authoring/data/apiHooks'; import { getLibraryId } from '../../generic/key-utils'; - -/** @typedef {import("../../taxonomy/data/types.js").TagListData} TagListData */ -/** @typedef {import("../../taxonomy/data/types.js").TagData} TagData */ +import { UpdateTagsData } from './types'; /** * Builds the query to get the taxonomy tags - * @param {number} taxonomyId The id of the taxonomy to fetch tags for - * @param {string|null} parentTag The tag whose children we're loading, if any - * @param {string} searchTerm The term passed in to perform search on tags - * @param {number} numPages How many pages of tags to load at this level + * @param taxonomyId The id of the taxonomy to fetch tags for + * @param parentTag The tag whose children we're loading, if any + * @param searchTerm The term passed in to perform search on tags + * @param numPages How many pages of tags to load at this level */ -export const useTaxonomyTagsData = (taxonomyId, parentTag = null, numPages = 1, searchTerm = '') => { +export const useTaxonomyTagsData = (taxonomyId: number, parentTag: string | null = null, numPages = 1, searchTerm = '') => { const queryClient = useQueryClient(); const queryFn = async ({ queryKey }) => { @@ -36,8 +34,7 @@ export const useTaxonomyTagsData = (taxonomyId, parentTag = null, numPages = 1, return getTaxonomyTagsData(taxonomyId, { parentTag: parentTag || '', searchTerm, page }); }; - /** @type {{queryKey: any[], queryFn: typeof queryFn, staleTime: number}[]} */ - const queries = []; + const queries: { queryKey: any[]; queryFn: typeof queryFn; staleTime: number }[] = []; for (let page = 1; page <= numPages; page++) { queries.push( { queryKey: ['taxonomyTags', taxonomyId, parentTag, page, searchTerm], queryFn, staleTime: Infinity }, @@ -54,8 +51,7 @@ export const useTaxonomyTagsData = (taxonomyId, parentTag = null, numPages = 1, const preLoadedData = new Map(); const newTags = dataPages.map(result => { - /** @type {TagData[]} */ - const simplifiedTagsList = []; + const simplifiedTagsList: TagData[] = []; result.data?.results?.forEach((tag) => { if (tag.parentValue === parentTag) { @@ -73,8 +69,7 @@ export const useTaxonomyTagsData = (taxonomyId, parentTag = null, numPages = 1, // Store the pre-loaded descendants into the query cache: preLoadedData.forEach((tags, parentValue) => { const queryKey = ['taxonomyTags', taxonomyId, parentValue, 1, searchTerm]; - /** @type {TagListData} */ - const cachedData = { + const cachedData: TagListData = { next: '', previous: '', count: tags.length, @@ -101,9 +96,9 @@ export const useTaxonomyTagsData = (taxonomyId, parentTag = null, numPages = 1, /** * Builds the query to get the taxonomy tags applied to the content object - * @param {string} contentId The ID of the content object to fetch the applied tags for (e.g. an XBlock usage key) + * @param contentId The ID of the content object to fetch the applied tags for (e.g. an XBlock usage key) */ -export const useContentTaxonomyTagsData = (contentId) => ( +export const useContentTaxonomyTagsData = (contentId: string) => ( useQuery({ queryKey: ['contentTaxonomyTags', contentId], queryFn: () => getContentTaxonomyTagsData(contentId), @@ -112,10 +107,10 @@ export const useContentTaxonomyTagsData = (contentId) => ( /** * Builds the query to get meta data about the content object - * @param {string} contentId The id of the content object - * @param {boolean} enabled Flag to enable/disable the query + * @param contentId The id of the content object + * @param enabled Flag to enable/disable the query */ -export const useContentData = (contentId, enabled) => ( +export const useContentData = (contentId: string, enabled: boolean) => ( useQuery({ queryKey: ['contentData', contentId], queryFn: enabled ? () => getContentData(contentId) : undefined, @@ -125,24 +120,17 @@ export const useContentData = (contentId, enabled) => ( /** * Builds the mutation to update the tags applied to the content object - * @param {string} contentId The id of the content object to update tags for + * @param contentId The id of the content object to update tags for */ -export const useContentTaxonomyTagsUpdater = (contentId) => { +export const useContentTaxonomyTagsUpdater = (contentId: string) => { const queryClient = useQueryClient(); const unitIframe = window.frames['xblock-iframe']; const { containerId } = useParams(); return useMutation({ - /** - * @type {import("@tanstack/react-query").MutateFunction< - * any, - * any, - * { - * tagsData: Promise - * } - * >} - */ - mutationFn: ({ tagsData }) => updateContentTaxonomyTags(contentId, tagsData), + mutationFn: ({ tagsData }: { tagsData: Promise }) => ( + updateContentTaxonomyTags(contentId, tagsData) + ), onSettled: () => { queryClient.invalidateQueries({ queryKey: ['contentTaxonomyTags', contentId] }); /// Invalidate query with pattern on course outline @@ -157,13 +145,13 @@ export const useContentTaxonomyTagsUpdater = (contentId) => { // Obtain library id from contentId const libraryId = getLibraryId(contentId); // Invalidate component metadata to update tags count - queryClient.invalidateQueries(xblockQueryKeys.componentMetadata(contentId)); + queryClient.invalidateQueries({ queryKey: xblockQueryKeys.componentMetadata(contentId) }); // Invalidate content search to update tags count - queryClient.invalidateQueries(['content_search'], { predicate: (query) => libraryQueryPredicate(query, libraryId) }); + queryClient.invalidateQueries({ queryKey: ['content_search'], predicate: (query) => libraryQueryPredicate(query, libraryId) }); // If the tags for an item were edited from a container page (Unit, Subsection, Section), // invalidate children query to fetch count again. if (containerId) { - queryClient.invalidateQueries(libraryAuthoringQueryKeys.containerChildren(containerId)); + queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.containerChildren(containerId) }); } } }, diff --git a/src/course-libraries/OutOfSyncAlert.tsx b/src/course-libraries/OutOfSyncAlert.tsx index da36c4086..5a365a273 100644 --- a/src/course-libraries/OutOfSyncAlert.tsx +++ b/src/course-libraries/OutOfSyncAlert.tsx @@ -32,14 +32,14 @@ export const OutOfSyncAlert: React.FC = ({ onReview, }) => { const intl = useIntl(); - const { data, isLoading } = useEntityLinksSummaryByDownstreamContext(courseId); + const { data, isPending } = useEntityLinksSummaryByDownstreamContext(courseId); const outOfSyncCount = data?.reduce((count, lib) => count + (lib.readyToSyncCount || 0), 0); const lastPublishedDate = data?.map(lib => new Date(lib.lastPublishedAt || 0).getTime()) .reduce((acc, lastPublished) => Math.max(lastPublished, acc), 0); const alertKey = `outOfSyncCountAlert-${courseId}`; useEffect(() => { - if (isLoading) { + if (isPending) { return; } if (outOfSyncCount === 0) { @@ -50,7 +50,7 @@ export const OutOfSyncAlert: React.FC = ({ const dismissedAlertDate = parseInt(localStorage.getItem(alertKey) ?? '0', 10); setShowAlert((lastPublishedDate ?? 0) > dismissedAlertDate); - }, [outOfSyncCount, lastPublishedDate, isLoading, data]); + }, [outOfSyncCount, lastPublishedDate, isPending, data]); const dismissAlert = () => { setShowAlert(false); diff --git a/src/course-libraries/ReviewTabContent.tsx b/src/course-libraries/ReviewTabContent.tsx index 03bbe6f2d..1d613e098 100644 --- a/src/course-libraries/ReviewTabContent.tsx +++ b/src/course-libraries/ReviewTabContent.tsx @@ -314,7 +314,7 @@ const ReviewTabContent = ({ courseId }: Props) => { const intl = useIntl(); const { data: outOfSyncItems, - isLoading: isSyncItemsLoading, + isPending: isSyncItemsLoading, isError, error, } = useEntityLinks({ diff --git a/src/data/apiHooks.ts b/src/data/apiHooks.ts index 709f9d315..8c81669a3 100644 --- a/src/data/apiHooks.ts +++ b/src/data/apiHooks.ts @@ -8,7 +8,7 @@ import { getWaffleFlags, waffleFlagDefaults } from './api'; export const useWaffleFlags = (courseId?: string) => { const queryClient = useQueryClient(); - const { data, isLoading, isError } = useQuery({ + const { data, isPending: isLoading, isError } = useQuery({ queryKey: ['waffleFlags', courseId], queryFn: () => getWaffleFlags(courseId), // Waffle flags change rarely, so never bother refetching them: diff --git a/src/generic/clipboard/hooks/useClipboard.ts b/src/generic/clipboard/hooks/useClipboard.ts index 63cff384e..1542cc126 100644 --- a/src/generic/clipboard/hooks/useClipboard.ts +++ b/src/generic/clipboard/hooks/useClipboard.ts @@ -34,7 +34,7 @@ const useClipboard = (canEdit: boolean = true) => { const { data: clipboardData } = useQuery({ queryKey: ['clipboard'], queryFn: getClipboard, - refetchInterval: (data) => (data?.content?.status === CLIPBOARD_STATUS.loading ? 1000 : false), + refetchInterval: (query) => (query.state.data?.content?.status === CLIPBOARD_STATUS.loading ? 1000 : false), }); const { showToast } = useContext(ToastContext); diff --git a/src/library-authoring/add-content/AddContent.tsx b/src/library-authoring/add-content/AddContent.tsx index ad5023a4b..65b8d13cc 100644 --- a/src/library-authoring/add-content/AddContent.tsx +++ b/src/library-authoring/add-content/AddContent.tsx @@ -478,7 +478,7 @@ const AddContent = () => { }; /* istanbul ignore next */ - if (pasteClipboardMutation.isLoading) { + if (pasteClipboardMutation.isPending) { showToast(intl.formatMessage(messages.pastingClipboardMessage)); } diff --git a/src/library-authoring/collections/LibraryCollectionPage.tsx b/src/library-authoring/collections/LibraryCollectionPage.tsx index bfdc1d4ef..2b54615bd 100644 --- a/src/library-authoring/collections/LibraryCollectionPage.tsx +++ b/src/library-authoring/collections/LibraryCollectionPage.tsx @@ -112,12 +112,12 @@ const LibraryCollectionPage = () => { const { data: collectionData, - isLoading, + isPending: isLoading, isError, error, } = useCollection(libraryId, collectionId); - const { data: libraryData, isLoading: isLibLoading } = useContentLibrary(libraryId); + const { data: libraryData, isPending: isLibLoading } = useContentLibrary(libraryId); if (!collectionId || !libraryId) { // istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker. diff --git a/src/library-authoring/component-info/ComponentAdvancedInfo.tsx b/src/library-authoring/component-info/ComponentAdvancedInfo.tsx index 924cde59b..e1dbcea9b 100644 --- a/src/library-authoring/component-info/ComponentAdvancedInfo.tsx +++ b/src/library-authoring/component-info/ComponentAdvancedInfo.tsx @@ -78,10 +78,10 @@ const ComponentAdvancedInfoInner: React.FC> = () => { { isEditingOLX ? ( <> - - diff --git a/src/library-authoring/component-info/ComponentDetails.tsx b/src/library-authoring/component-info/ComponentDetails.tsx index 84bdffe4b..965e1235e 100644 --- a/src/library-authoring/component-info/ComponentDetails.tsx +++ b/src/library-authoring/component-info/ComponentDetails.tsx @@ -24,14 +24,14 @@ const ComponentDetails = () => { data: componentMetadata, isError, error, - isLoading, + isPending, } = useLibraryBlockMetadata(usageKey); if (isError) { return ; } - if (isLoading) { + if (isPending) { return ; } diff --git a/src/library-authoring/component-picker/ComponentPicker.test.tsx b/src/library-authoring/component-picker/ComponentPicker.test.tsx index f7c3558a5..5e9bd9373 100644 --- a/src/library-authoring/component-picker/ComponentPicker.test.tsx +++ b/src/library-authoring/component-picker/ComponentPicker.test.tsx @@ -129,7 +129,10 @@ describe('', () => { // Wait for the content library to load await screen.findByText(/Change Library/i); - expect(await screen.findByText('Test Library 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('Test Library 1')).toBeInTheDocument(); + expect(screen.queryAllByText('Introduction to Testing')[0]).toBeInTheDocument(); + }); // Click on the component card to open the sidebar fireEvent.click(screen.queryAllByText('Introduction to Testing')[0]); @@ -173,7 +176,10 @@ describe('', () => { // Wait for the content library to load await screen.findByText(/Change Library/i); - expect(await screen.findByText('Test Library 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('Test Library 1')).toBeInTheDocument(); + expect(screen.queryAllByText('Collection 1')[0]).toBeInTheDocument(); + }); // Mock the collection search result mockSearchResult(mockCollectionResult); @@ -194,7 +200,10 @@ describe('', () => { // Wait for the content library to load await screen.findByText(/Change Library/i); - expect(await screen.findByText('Test Library 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('Test Library 1')).toBeInTheDocument(); + expect(screen.queryAllByText('Collection 1')[0]).toBeInTheDocument(); + }); // Click on the collection card to open the sidebar fireEvent.click(screen.queryAllByText('Collection 1')[0]); @@ -229,7 +238,10 @@ describe('', () => { // Wait for the content library to load await screen.findByText(/Change Library/i); - expect(await screen.findByText('Test Library 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('Test Library 1')).toBeInTheDocument(); + expect(screen.queryAllByText('Collection 1')[0]).toBeInTheDocument(); + }); // Click on the collection card to open the sidebar fireEvent.click(screen.queryAllByText('Collection 1')[0]); @@ -283,7 +295,10 @@ describe('', () => { // Wait for the content library to load await screen.findByText(/Change Library/i); - expect(await screen.findByText('Test Library 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('Test Library 1')).toBeInTheDocument(); + expect(screen.queryAllByRole('button', { name: 'Select' })[0]).toBeInTheDocument(); + }); // Select the first component fireEvent.click(screen.queryAllByRole('button', { name: 'Select' })[0]); @@ -330,7 +345,10 @@ describe('', () => { // Wait for the content library to load await screen.findByText(/Change Library/i); - expect(await screen.findByText('Test Library 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('Test Library 1')).toBeInTheDocument(); + expect(screen.queryAllByText('Introduction to Testing')[0]).toBeInTheDocument(); + }); // Click on the component card to open the sidebar fireEvent.click(screen.queryAllByText('Introduction to Testing')[0]); @@ -413,7 +431,10 @@ describe('', () => { // Wait for the content library to load await screen.findByText(/Change Library/i); - expect(await screen.findByText('Test Library 1')).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText('Test Library 1')).toBeInTheDocument(); + expect(screen.queryAllByText('Collection 1')[0]).toBeInTheDocument(); + }); // Click on the collection card to open the sidebar fireEvent.click(screen.queryAllByText('Collection 1')[0]); diff --git a/src/library-authoring/component-picker/SelectLibrary.tsx b/src/library-authoring/component-picker/SelectLibrary.tsx index fc914c348..d9f2e0a19 100644 --- a/src/library-authoring/component-picker/SelectLibrary.tsx +++ b/src/library-authoring/component-picker/SelectLibrary.tsx @@ -57,7 +57,7 @@ const SelectLibrary = ({ selectedLibrary, setSelectedLibrary, itemType }: Select const { data, - isLoading, + isPending, isError, error, } = useContentLibraryV2List({ @@ -70,7 +70,7 @@ const SelectLibrary = ({ selectedLibrary, setSelectedLibrary, itemType }: Select return ; } - if (isLoading) { + if (isPending) { return ; } diff --git a/src/library-authoring/create-library/CreateLibrary.tsx b/src/library-authoring/create-library/CreateLibrary.tsx index e88c4ffa0..ada04427e 100644 --- a/src/library-authoring/create-library/CreateLibrary.tsx +++ b/src/library-authoring/create-library/CreateLibrary.tsx @@ -32,7 +32,7 @@ const CreateLibrary = () => { const { mutate, data, - isLoading, + isPending, isError, error, } = useCreateLibraryV2(); @@ -149,7 +149,7 @@ const CreateLibrary = () => { type="submit" variant="primary" className="action btn-primary" - state={isLoading ? 'disabled' : 'enabled'} + state={isPending ? 'disabled' : 'enabled'} disabledStates={['disabled']} labels={{ enabled: intl.formatMessage(messages.createLibraryButton), diff --git a/src/library-authoring/data/apiHooks.ts b/src/library-authoring/data/apiHooks.ts index 9dd891e13..64e571d94 100644 --- a/src/library-authoring/data/apiHooks.ts +++ b/src/library-authoring/data/apiHooks.ts @@ -6,6 +6,7 @@ import { type Query, type QueryClient, replaceEqualDeep, + keepPreviousData, } from '@tanstack/react-query'; import { useCallback } from 'react'; import { type MeiliSearch } from 'meilisearch'; @@ -201,7 +202,7 @@ export const useUpdateLibraryMetadata = () => { mutationFn: api.updateLibraryMetadata, onMutate: async (data) => { const queryKey = libraryAuthoringQueryKeys.contentLibrary(data.id); - const previousLibraryData = queryClient.getQueriesData(queryKey)[0][1] as api.ContentLibrary; + const previousLibraryData = queryClient.getQueriesData({ queryKey })[0][1] as api.ContentLibrary; const newLibraryData = { ...previousLibraryData, @@ -231,7 +232,7 @@ export const useContentLibraryV2List = (customParams: api.GetLibrariesV2CustomPa useQuery({ queryKey: libraryAuthoringQueryKeys.contentLibraryList(customParams), queryFn: () => api.getContentLibraryV2List(customParams), - keepPreviousData: true, + placeholderData: keepPreviousData, }) ); @@ -366,7 +367,7 @@ export const useUpdateXBlockFields = (usageKey: string) => { mutationFn: (data: api.UpdateXBlockFieldsRequest) => api.updateXBlockFields(usageKey, data), onMutate: async (data) => { const queryKey = xblockQueryKeys.xblockFields(usageKey); - const previousBlockData = queryClient.getQueriesData(queryKey)?.[0]?.[1] as api.XBlockFields | undefined; + const previousBlockData = queryClient.getQueriesData({ queryKey })?.[0]?.[1] as api.XBlockFields | undefined; const formatedData = camelCaseObject(data); if (!previousBlockData) { diff --git a/src/library-authoring/hierarchy/ItemHierarchy.tsx b/src/library-authoring/hierarchy/ItemHierarchy.tsx index 58f7599a7..eccd2179f 100644 --- a/src/library-authoring/hierarchy/ItemHierarchy.tsx +++ b/src/library-authoring/hierarchy/ItemHierarchy.tsx @@ -87,11 +87,11 @@ export const ItemHierarchy = ({ const { data, - isLoading, + isPending, isError, } = useLibraryItemHierarchy(itemId); - if (isLoading) { + if (isPending) { return ; } diff --git a/src/library-authoring/hierarchy/ItemHierarchyPublisher.tsx b/src/library-authoring/hierarchy/ItemHierarchyPublisher.tsx index b86079578..6df4b418b 100644 --- a/src/library-authoring/hierarchy/ItemHierarchyPublisher.tsx +++ b/src/library-authoring/hierarchy/ItemHierarchyPublisher.tsx @@ -31,11 +31,11 @@ export const ItemHierarchyPublisher = ({ const { data: hierarchy, - isLoading, + isPending, isError, } = useLibraryItemHierarchy(itemId); - if (isLoading) { + if (isPending) { return ; } diff --git a/src/search-manager/data/apiHooks.test.tsx b/src/search-manager/data/apiHooks.test.tsx index 4a7d7c878..b347d2e95 100644 --- a/src/search-manager/data/apiHooks.test.tsx +++ b/src/search-manager/data/apiHooks.test.tsx @@ -41,7 +41,7 @@ describe('search manager api hooks', () => { fetchMockResponse(); const { result } = renderHook(() => useGetBlockTypes('filter'), { wrapper }); await waitFor(() => { - expect(result.current.isLoading).toBeFalsy(); + expect(result.current.isPending).toBeFalsy(); }); const expectedData = { chapter: 1, diff --git a/src/search-manager/data/apiHooks.ts b/src/search-manager/data/apiHooks.ts index a2ee0b6c3..821aece67 100644 --- a/src/search-manager/data/apiHooks.ts +++ b/src/search-manager/data/apiHooks.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; +import { keepPreviousData, useInfiniteQuery, useQuery } from '@tanstack/react-query'; import { type Filter, MeiliSearch } from 'meilisearch'; import { @@ -26,7 +26,7 @@ export const useContentSearchConnection = (): { const { data: connectionDetails, isError: hasConnectionError } = useQuery({ queryKey: ['content_search'], queryFn: getContentSearchConfig, - cacheTime: 60 * 60_000, // Even if we're not actively using the search modal, keep it in memory up to an hour + gcTime: 60 * 60_000, // Even if we're not actively using the search modal, keep it in memory up to an hour staleTime: 60 * 60_000, // If cache is up to one hour old, no need to re-fetch refetchInterval: 60 * 60_000, refetchOnWindowFocus: false, // This doesn't need to be refreshed when the user switches back to this tab. @@ -135,7 +135,7 @@ export const useContentSearchResults = ({ tagsFilter, sort, }), - queryFn: ({ pageParam = 0 }) => { + queryFn: ({ pageParam }) => { // istanbul ignore if: this should never happen if (client === undefined || indexName === undefined) { throw new Error('Required data unexpectedly undefined. Check "enable" condition of useQuery.'); @@ -157,9 +157,10 @@ export const useContentSearchResults = ({ limit, }); }, + initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.nextOffset, // Avoid flickering results when user is typing... keep old results until new is available. - keepPreviousData: true, + placeholderData: keepPreviousData, refetchOnWindowFocus: false, // This doesn't need to be refreshed when the user switches back to this tab. }); @@ -176,7 +177,7 @@ export const useContentSearchResults = ({ problemTypes: pages?.[0]?.problemTypes ?? {}, publishStatus: pages?.[0]?.publishStatus ?? {}, status: query.status, - isLoading: query.isLoading, + isLoading: query.isPending, isError: query.isError, error: query.error, isFetchingNextPage: query.isFetchingNextPage, @@ -232,7 +233,7 @@ export const useTagFilterOptions = (args: { return fetchAvailableTagOptions({ ...args, client, indexName }); }, // Avoid flickering results when user is typing... keep old results until new is available. - keepPreviousData: true, + placeholderData: keepPreviousData, refetchOnWindowFocus: false, // This doesn't need to be refreshed when the user switches back to this tab. }); @@ -257,7 +258,7 @@ export const useTagFilterOptions = (args: { return fetchTagsThatMatchKeyword({ ...args, client, indexName }); }, // Avoid flickering results when user is typing... keep old results until new is available. - keepPreviousData: true, + placeholderData: keepPreviousData, refetchOnWindowFocus: false, // This doesn't need to be refreshed when the user switches back to this tab. }); diff --git a/src/taxonomy/manage-orgs/data/api.js b/src/taxonomy/manage-orgs/data/api.ts similarity index 71% rename from src/taxonomy/manage-orgs/data/api.js rename to src/taxonomy/manage-orgs/data/api.ts index 499aa3c73..795898297 100644 --- a/src/taxonomy/manage-orgs/data/api.js +++ b/src/taxonomy/manage-orgs/data/api.ts @@ -1,15 +1,10 @@ -// @ts-check import { camelCaseObject, getConfig } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { useQueryClient, useMutation } from '@tanstack/react-query'; const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL; -/** - * @param {number} taxonomyId - * @returns {string} - */ -export const getManageOrgsApiUrl = (taxonomyId) => new URL( +export const getManageOrgsApiUrl = (taxonomyId: number): string => new URL( `api/content_tagging/v1/taxonomies/${taxonomyId}/orgs/`, getApiBaseUrl(), ).href; @@ -20,18 +15,7 @@ export const getManageOrgsApiUrl = (taxonomyId) => new URL( export const useManageOrgs = () => { const queryClient = useQueryClient(); return useMutation({ - /** - * @type {import("@tanstack/react-query").MutateFunction< - * any, - * any, - * { - * taxonomyId: number, - * orgs?: string[], - * allOrgs: boolean, - * } - * >} - */ - mutationFn: async ({ taxonomyId, orgs, allOrgs }) => { + mutationFn: async ({ taxonomyId, orgs, allOrgs }: { taxonomyId: number, orgs?: string[], allOrgs: boolean }) => { const { data } = await getAuthenticatedHttpClient().put( getManageOrgsApiUrl(taxonomyId), { diff --git a/src/taxonomy/tag-list/TagListTable.jsx b/src/taxonomy/tag-list/TagListTable.jsx index 013fcb653..16cc96387 100644 --- a/src/taxonomy/tag-list/TagListTable.jsx +++ b/src/taxonomy/tag-list/TagListTable.jsx @@ -12,7 +12,7 @@ import { useTagListData, useSubTags } from '../data/apiHooks'; const SubTagsExpanded = ({ taxonomyId, parentTagValue }) => { const subTagsData = useSubTags(taxonomyId, parentTagValue); - if (subTagsData.isLoading) { + if (subTagsData.isPending) { return ; } if (subTagsData.isError) {