mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-30 21:49:18 +01:00
fix(ui): stop refetching cache-only queries without a queryFn (#18062)
Task snapshots are cached under each alias key (UUID and identifier) with setQueryData. Those entries have no queryFn until a view observes them, so inactive invalidations refetched them and failed with "Missing queryFn". The app QueryClient now skips entries without a queryFn when it refetches; invalidateQueries still marks them stale. Fixes PAPERCLIP-FRONTEND-X Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a30194e008
commit
a39f1b81f1
2 files changed
+56
No files matched your search
@@ -275,4 +275,43 @@ describe("createAppQueryClient", () => {
|
||||
expect(reportError).toHaveBeenCalledTimes(1);
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it("leaves a cache-only alias stale instead of refetching it without a queryFn", async () => {
|
||||
client.setQueryData(["issues", "detail", "issue-uuid"], { id: "issue-uuid", title: "Seeded" });
|
||||
const queryFn = vi.fn(async () => "fetched");
|
||||
client.setQueryData(["issues", "detail", "PAP-1"], "seeded");
|
||||
await client.prefetchQuery({ queryKey: ["issues", "detail", "PAP-1"], queryFn, staleTime: 0 });
|
||||
queryFn.mockClear();
|
||||
|
||||
void client.invalidateQueries({ queryKey: ["issues", "detail"], refetchType: "inactive" });
|
||||
void client.refetchQueries({ queryKey: ["issues", "detail"], type: "all" });
|
||||
await vi.advanceTimersByTimeAsync(60_000);
|
||||
|
||||
const alias = client.getQueryCache().find({ queryKey: ["issues", "detail", "issue-uuid"], exact: true });
|
||||
expect(alias?.state.status).toBe("success");
|
||||
expect(alias?.state.isInvalidated).toBe(true);
|
||||
expect(alias?.state.data).toEqual({ id: "issue-uuid", title: "Seeded" });
|
||||
expect(queryFn).toHaveBeenCalledTimes(2);
|
||||
expect(reportError).not.toHaveBeenCalled();
|
||||
|
||||
const aliasQueryFn = vi.fn(async () => ({ id: "issue-uuid", title: "Fresh" }));
|
||||
const observer = new QueryObserver(client, { queryKey: ["issues", "detail", "issue-uuid"], queryFn: aliasQueryFn });
|
||||
const unsubscribe = observer.subscribe(() => {});
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
expect(aliasQueryFn).toHaveBeenCalledTimes(1);
|
||||
expect(observer.getCurrentResult().data).toEqual({ id: "issue-uuid", title: "Fresh" });
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it("keeps the caller's refetch predicate", async () => {
|
||||
const keep = vi.fn(async () => "keep");
|
||||
const skip = vi.fn(async () => "skip");
|
||||
await client.prefetchQuery({ queryKey: ["keep"], queryFn: keep });
|
||||
await client.prefetchQuery({ queryKey: ["skip"], queryFn: skip });
|
||||
|
||||
await client.refetchQueries({ predicate: (query) => query.queryKey[0] === "keep" });
|
||||
|
||||
expect(keep).toHaveBeenCalledTimes(2);
|
||||
expect(skip).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -24,6 +24,9 @@ import {
|
||||
type Mutation,
|
||||
type MutationOptions,
|
||||
type QueryClientConfig,
|
||||
type QueryKey,
|
||||
type RefetchOptions,
|
||||
type RefetchQueryFilters,
|
||||
} from "@tanstack/react-query";
|
||||
import { classifyError, describeError, errorRetryAfterMs, isTransientError } from "@/api/errors";
|
||||
import { MAX_PROBE_DELAY_MS, PROBE_BACKOFF_MS, type ConnectivityStore } from "./connectivity";
|
||||
@@ -144,6 +147,20 @@ class AppQueryClient extends QueryClient {
|
||||
retryDelay: options?.retryDelay ?? retryDelayFor,
|
||||
};
|
||||
}
|
||||
|
||||
override refetchQueries<TTaggedQueryKey extends QueryKey = QueryKey>(
|
||||
filters?: RefetchQueryFilters<TTaggedQueryKey>,
|
||||
options?: RefetchOptions,
|
||||
): Promise<void> {
|
||||
const predicate = filters?.predicate;
|
||||
return super.refetchQueries(
|
||||
{
|
||||
...filters,
|
||||
predicate: (query) => query.options.queryFn !== undefined && (predicate?.(query) ?? true),
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** A polling query that keeps failing reports once per window, not on every poll. */
|
||||
|
||||
Reference in new issue
Block a user