From harness-claude
Directly read, write, remove, and manage TanStack Query cache entries using QueryClient methods for mutations, WebSockets, logout, and testing.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Directly read, write, and remove cache entries with QueryClient methods
Controls TanStack Query cache freshness and invalidation using invalidateQueries, staleTime, gcTime, and refetch strategies for post-mutation refreshes and data lifecycle management.
Implements TanStack Query v5 in React apps for API data fetching, server state caching, mutations, optimistic updates, infinite scroll, streaming AI responses, and tRPC v11 integration.
Provides expertise in TanStack Query for React/Next.js data fetching, caching (staleTime/gcTime), mutations, optimistic updates, cache invalidation, and App Router SSR hydration.
Share bugs, ideas, or general feedback.
Directly read, write, and remove cache entries with QueryClient methods
queryClient.setQueryData(key, updaterOrValue) to write a value directly to the cache — the query does not refetch.queryClient.getQueryData(key) to read a cached value synchronously — returns undefined if not cached.queryClient.cancelQueries({ queryKey }) before manual cache writes to prevent in-flight fetches from overwriting your change.queryClient.removeQueries({ queryKey }) to evict cache entries entirely — they refetch fresh on next mount.queryClient.clear() to clear the entire cache — call this on logout to remove all user data.queryClient.getQueriesData(filter) to bulk-read multiple cache entries matching a filter.setQueryData for immutable updates — receive current value, return new value.// Real-time cache update from WebSocket
function useLivePostUpdates() {
const queryClient = useQueryClient();
useEffect(() => {
const ws = new WebSocket('/ws/posts');
ws.onmessage = (event) => {
const updatedPost: Post = JSON.parse(event.data);
// Update the specific post detail
queryClient.setQueryData(postKeys.detail(updatedPost.id), updatedPost);
// Update the post in all list caches
queryClient.setQueriesData<PostsPage>({ queryKey: postKeys.lists() }, (old) => {
if (!old) return old;
return {
...old,
posts: old.posts.map((p) => (p.id === updatedPost.id ? updatedPost : p)),
};
});
};
return () => ws.close();
}, [queryClient]);
}
// Logout — clear all user data from cache
function useLogout() {
const queryClient = useQueryClient();
const router = useRouter();
return async () => {
await fetch('/api/auth/logout', { method: 'POST' });
queryClient.clear(); // remove all cached data
router.push('/login');
};
}
// Seeding cache after create mutation — avoid refetch
const { mutate: createPost } = useMutation({
mutationFn: (data: CreatePostInput) =>
fetch('/api/posts', { method: 'POST', body: JSON.stringify(data) }).then((r) => r.json()),
onSuccess: (newPost: Post) => {
// Add to detail cache directly
queryClient.setQueryData(postKeys.detail(newPost.id), newPost);
// Invalidate lists — server determines list membership
queryClient.invalidateQueries({ queryKey: postKeys.lists() });
},
});
TanStack Query's cache is a Map-like structure keyed by serialized query keys. setQueryData, getQueryData, and removeQueries give direct programmatic access to this cache outside the normal fetch lifecycle.
setQueryData vs invalidateQueries: setQueryData writes a value and marks the query as fresh (it will not refetch until staleTime expires). invalidateQueries marks the query as stale and triggers a background refetch. Use setQueryData when you have the correct server response in hand (e.g., from a create mutation); use invalidateQueries when you know data has changed but do not have the new value.
setQueriesData for list updates: When a single entity update needs to reflect in multiple list queries (e.g., filtered views), use setQueriesData(filter, updater) to update all matching caches in one call. The filter uses the same matching logic as invalidateQueries.
Observer pattern: Use queryClient.getQueryCache().subscribe(callback) to observe all cache changes. This is useful for syncing query state to external systems (analytics, debug logging). Unsubscribe on cleanup.
Type safety: getQueryData<T>(key) returns T | undefined. Always handle the undefined case — the cache may not have the value if it was never fetched or was garbage collected.
Testing assertions: In tests, call queryClient.getQueryData(key) after mutations to assert that the cache was updated correctly — without needing to render a component and inspect the DOM.
https://tanstack.com/query/latest/docs/reference/QueryClient