TanStack / query

🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.
https://tanstack.com/query
MIT License
42.72k stars 2.93k forks source link

[svelte-query] prefetchQuery fails to cache if `url` object is accessed inside `layout.ts` #8196

Closed maisonsmd closed 1 month ago

maisonsmd commented 1 month ago

Describe the bug

This is kinda weird and oddly specific issue, in layout.ts, if I access the url object from the load() arguments, prefetchQuery fails to cache the result.

If I comment out the access statement, everything works fine.

Your minimal, reproducible example

https://codesandbox.io/p/github/maisonsmd/tanstack-sveltequery-prefetch-issue/main?import=true

Steps to reproduce

  1. Run project
  2. Check prefetchQuery (page.ts) function Case A. Hover on post list's items Case B. Click on a post
  3. Check network tab and Query Devtool for cache

Expected behavior

Case A

Expect: Only 1 API call needed per post, the response is cached Actual: the response is not cached, re-hovering on the same item fetches the API again.

Case B

Expect: Only 1 API call needed per post, the response is cached, clicking on the post does not result in additional API call. Actual: API is called 2 times per post as the result of failing to cache the previous call.

How often does this bug happen?

Every time

Screenshots or Videos

https://github.com/user-attachments/assets/7f5dd419-ab6e-4311-a313-986a6d3d16e8

Platform

OS: Windows 11 Pro 23H2 build 22631.4317 Browser: Microsoft Edge 129.0.2792.89 (64-bit)

Tanstack Query adapter

svelte-query

TanStack Query version

v5.59.13

TypeScript version

v5.3.3

Additional context

staleTime was set to 60 seconds.

maisonsmd commented 1 month ago

I found the culprit.

With the reproduction +layout.ts:

import { QueryClient } from "@tanstack/svelte-query"
import type { LayoutLoad } from "./$types"
import { browser } from "$app/environment"

export const load: LayoutLoad = async ({ url }) => {
  console.log(url.pathname)

  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        enabled: browser,
        staleTime: 60 * 1000,
      },
    },
  })

  return { queryClient }
}

When accessing the url, sveltekit will call this layout load function for every page to update the url parameter, which then create multiple queryClients that has nothing to do with each other, hence no cached shared.

Without the url access line, I think there's some kind of optimization that Sveltekit won't call load because nothing it used has changed.

The fix is not to use anything that depends on each route or page like url inside the root layout to prevent it from being reloaded. You can move them into child +page.ts or +layout.ts.