diff --git a/.changeset/little-melons-grow.md b/.changeset/little-melons-grow.md new file mode 100644 index 0000000000..1d9cb5e3f2 --- /dev/null +++ b/.changeset/little-melons-grow.md @@ -0,0 +1,5 @@ +--- +'@redux-devtools/rtk-query-monitor': minor +--- + +Handle api.provided state changes in RTKQ 2.6.2 diff --git a/packages/redux-devtools-rtk-query-monitor/src/selectors.ts b/packages/redux-devtools-rtk-query-monitor/src/selectors.ts index ed2873ce6c..2e17bb402c 100644 --- a/packages/redux-devtools-rtk-query-monitor/src/selectors.ts +++ b/packages/redux-devtools-rtk-query-monitor/src/selectors.ts @@ -6,9 +6,10 @@ import { RtkQueryApiState, RtkQueryTag, SelectorsSource, - RtkQueryProvided, + RtkQueryProvidedTagsState, QueryPreviewTabs, RtkResourceInfo, + RtkQuery262ProvidedState, } from './types'; import { Comparator, queryComparators } from './utils/comparators'; import { FilterList, queryListFilters } from './utils/filters'; @@ -216,7 +217,7 @@ export function createInspectorSelectors(): InspectorSelectors { const selectProvidedOfCurrentQuery: InspectorSelector< S, - null | RtkQueryProvided + null | RtkQueryProvidedTagsState | RtkQuery262ProvidedState > = (selectorsSource: SelectorsSource) => { return selectApiOfCurrentQuery(selectorsSource)?.provided ?? null; }; diff --git a/packages/redux-devtools-rtk-query-monitor/src/types.ts b/packages/redux-devtools-rtk-query-monitor/src/types.ts index 46f7896ecc..58998d835e 100644 --- a/packages/redux-devtools-rtk-query-monitor/src/types.ts +++ b/packages/redux-devtools-rtk-query-monitor/src/types.ts @@ -1,5 +1,9 @@ import type { LiftedAction, LiftedState } from '@redux-devtools/core'; -import type { createApi, QueryStatus } from '@reduxjs/toolkit/query'; +import type { + createApi, + QueryCacheKey, + QueryStatus, +} from '@reduxjs/toolkit/query'; import type { Action, AnyAction, Dispatch } from '@reduxjs/toolkit'; import type { ComponentType } from 'react'; import { base16Themes } from 'react-base16-styling'; @@ -52,7 +56,37 @@ export type RtkMutationState = NonNullable< export type RtkQueryApiConfig = RtkQueryApiState['config']; -export type RtkQueryProvided = RtkQueryApiState['provided']; +export type FullTagDescription = { + type: TagType; + id?: number | string; +}; + +// This is the actual tags structure, and was the entire `api.provided` +// field up through 2.6.1 +export type RtkQueryProvidedTagsState = { + [x: string]: { + [id: string]: QueryCacheKey[]; + [id: number]: QueryCacheKey[]; + }; +}; + +// As of 2.6.2, the `api.provided` field is split into `tags` and `keys` fields, +// with the old data nested in `tags`. +export type RtkQuery262ProvidedState = { + keys: Record[]>; + tags: RtkQueryProvidedTagsState; +}; + +export function isRtkQuery262Provided( + provided: Record, +): provided is RtkQuery262ProvidedState { + return ( + 'tags' in provided && + 'keys' in provided && + typeof provided.tags === 'object' && + typeof provided.keys === 'object' + ); +} export interface ExternalProps> { dispatch: Dispatch>; diff --git a/packages/redux-devtools-rtk-query-monitor/src/utils/rtk-query.ts b/packages/redux-devtools-rtk-query-monitor/src/utils/rtk-query.ts index 89b7dd281a..671064d61c 100644 --- a/packages/redux-devtools-rtk-query-monitor/src/utils/rtk-query.ts +++ b/packages/redux-devtools-rtk-query-monitor/src/utils/rtk-query.ts @@ -1,5 +1,5 @@ import { Action, AnyAction, isAllOf, isPlainObject } from '@reduxjs/toolkit'; -import { QueryStatus } from '@reduxjs/toolkit/query'; +import { QueryCacheKey, QueryStatus } from '@reduxjs/toolkit/query'; import { QueryInfo, RtkQueryMonitorState, @@ -11,7 +11,8 @@ import { MutationInfo, ApiStats, QueryTally, - RtkQueryProvided, + RtkQueryProvidedTagsState, + RtkQuery262ProvidedState, ApiTimings, QueryTimings, SelectorsSource, @@ -19,6 +20,7 @@ import { RtkResourceInfo, RtkRequest, RtkRequestTiming, + isRtkQuery262Provided, } from '../types'; import { missingTagId } from '../monitor-config'; import { Comparator, compareJSONPrimitive } from './comparators'; @@ -529,13 +531,25 @@ export function getProvidedOf( export function getQueryTagsOf( resInfo: RtkResourceInfo | null, - provided: RtkQueryProvided | null, + provided: RtkQueryProvidedTagsState | RtkQuery262ProvidedState | null, ): RtkQueryTag[] { if (!resInfo || resInfo.type === 'mutation' || !provided) { return emptyArray; } - const tagTypes = Object.keys(provided); + // Handle `api.provided` schema change with RTK Query tag handling. + // Originally, `api.provided` was a `Record>`, + // directly containing the tag names. + // With https://github.com/reduxjs/redux-toolkit/pull/4910 , that changes to + // change the top level to be `{tags, keys}`, with `tags` containing the tag names. + // Handle the newer structure by extracting the right field if it exists. + const actualProvided: RtkQueryProvidedTagsState = isRtkQuery262Provided( + provided, + ) + ? provided.tags + : provided; + + const tagTypes = Object.keys(actualProvided); if (tagTypes.length < 1) { return emptyArray; @@ -543,10 +557,10 @@ export function getQueryTagsOf( const output: RtkQueryTag[] = []; - for (const [type, tagIds] of Object.entries(provided)) { + for (const [type, tagIds] of Object.entries(actualProvided)) { if (tagIds) { for (const [id, queryKeys] of Object.entries(tagIds)) { - if ((queryKeys as unknown[]).includes(resInfo.queryKey)) { + if (queryKeys.includes(resInfo.queryKey as QueryCacheKey)) { const tag: RtkQueryTag = { type }; if (id !== missingTagId) {