fix(offline): include provided in RTKQ rehydration payload
RTK Query 2.12's invalidation slice reads `provided.tags` during cache
rehydration (`Object.entries(provided.tags ?? {})`). Our persisted
snapshot only carried `{ queries, mutations }`, so `provided` was
undefined and `.tags` threw on every startup with a cached snapshot —
crashing the app inside the rehydrate reducer / immer produce.
Snapshot now carries the real `provided` (so invalidation tags
rehydrate), and `load()` defaults it to `{ tags: {}, keys: {} }` so
snapshots persisted before this field existed recover without a manual
localStorage clear.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,11 @@ export const REHYDRATE_API = 'api/rehydrate';
|
||||
export interface RehydrateApiPayload {
|
||||
queries: Record<string, unknown>;
|
||||
mutations: Record<string, unknown>;
|
||||
// RTKQ's invalidation slice reads `provided.tags`/`provided.keys` during
|
||||
// rehydration (it does `Object.entries(provided.tags ?? {})`), so `provided`
|
||||
// must be an object — a bare `{ queries, mutations }` makes it crash on
|
||||
// `provided.tags` of undefined. Always present; empty objects are valid.
|
||||
provided: { tags: Record<string, unknown>; keys: Record<string, unknown> };
|
||||
}
|
||||
|
||||
export const rehydrateApi = createAction<RehydrateApiPayload>(REHYDRATE_API);
|
||||
|
||||
@@ -22,13 +22,17 @@ type QueryEntry = ApiState['queries'][string];
|
||||
* carry no usable data and subscriptions are rebuilt by components on mount.
|
||||
* Mutation results are never restored.
|
||||
*/
|
||||
const EMPTY_PROVIDED = { tags: {}, keys: {} };
|
||||
|
||||
function snapshot(apiState: ApiState): RehydrateApiPayload {
|
||||
const queries: Record<string, unknown> = {};
|
||||
for (const [key, entry] of Object.entries(apiState.queries)) {
|
||||
const q = entry as QueryEntry | undefined;
|
||||
if (q && q.status === 'fulfilled') queries[key] = q;
|
||||
}
|
||||
return { queries, mutations: {} };
|
||||
// Carry `provided` along so RTKQ can re-register invalidation tags for the
|
||||
// restored entries; it is also required structurally (see RehydrateApiPayload).
|
||||
return { queries, mutations: {}, provided: apiState.provided ?? EMPTY_PROVIDED };
|
||||
}
|
||||
|
||||
function load(): RehydrateApiPayload | null {
|
||||
@@ -37,7 +41,13 @@ function load(): RehydrateApiPayload | null {
|
||||
if (!raw) return null;
|
||||
const parsed = JSON.parse(raw) as Partial<RehydrateApiPayload>;
|
||||
if (!parsed.queries) return null;
|
||||
return { queries: parsed.queries, mutations: {} };
|
||||
// `provided` may be absent in snapshots written before this field existed —
|
||||
// default it so the invalidation slice doesn't crash on `provided.tags`.
|
||||
return {
|
||||
queries: parsed.queries,
|
||||
mutations: {},
|
||||
provided: parsed.provided ?? EMPTY_PROVIDED,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user