Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tanstack Query for Svelte 5 does not rerender when adding a dependency variable #7227

Open
Boniqx opened this issue Apr 5, 2024 · 8 comments

Comments

@Boniqx
Copy link

Boniqx commented Apr 5, 2024

Describe the bug

Issue Description:

Problem:
When utilizing the createInfiniteQuery client to manage infinite queries, there is an issue with updating the query despite the addition of a dependency variable in the query key.

Steps to Reproduce:

Initialize a query using createInfiniteQuery.
Define a queryKey that includes generic variables.
Implement the queryFn function to handle data retrieval based on certain conditions.
Attempt to update the query based on changes in the dependency variable within the queryKey.

Actual Behavior:
Despite adding the dependency variable to the queryKey, the query does not update as expected when the variable changes.

Code Snippet:

javascript

let query = createInfiniteQuery({
  queryKey: ['Resource', dependencyVariable],
  queryFn: async ({ pageParam }) => {
    // Implementation omitted for brevity
  },
  getNextPageParam(lastPage) {
    // Implementation omitted for brevity
  },
  initialPageParam: null as string | null | undefined,
  initialDataUpdatedAt: () => Date.now(),
});

Environment:

Framework/Libraries: SVELTE 5
Possible Solutions:

Review the implementation of createInfiniteQuery to ensure proper handling of dependency variables.
Check for any inconsistencies or errors in the query key setup.
Consider alternative approaches for managing infinite queries that may better accommodate dynamic updates.

Your minimal, reproducible example

I have added an example code below

Steps to reproduce

Steps to Reproduce:

Initialize a query using createInfiniteQuery.
Define a queryKey that includes generic variables.
Implement the queryFn function to handle data retrieval based on certain conditions.
Attempt to update the query based on changes in the dependency variable within the queryKey.
Expected Behavior:
The query should update accordingly when there are changes in the dependency variable included in the queryKey.

Expected behavior

Expected Behavior:
The query should update accordingly when there are changes in the dependency variable included in the queryKey.

How often does this bug happen?

Every time

Screenshots or Videos

test

Platform

Any platform

Tanstack Query adapter

None

TanStack Query version

@tanstack/svelte-query

TypeScript version

5.4.3

Additional context

Additional Context:
This issue impacts the ability to dynamically update queries based on changing dependency variables, hindering the functionality of the application.

@halafi
Copy link

halafi commented Apr 8, 2024

I have the same issue, I would expect infinite query to refetch page 0 with different parameters (or do anything at all) when part of the array key changes, as a temporary workaround I am using:

enabled: false

and manually triggering refetch in a reactive statement

$postQuery.refetch({ refetchPage: 0 });

@TkDodo
Copy link
Collaborator

TkDodo commented Apr 8, 2024

I have added an example code below

Please show a minimal reproduction with codesandbox or stackblitz

@nicksulkers
Copy link

nicksulkers commented Apr 11, 2024

To my knowledge, Tanstack Query has yet to support Svelte 5 runes, so directly feeding $state (assuming dependencyVariable is $state) won't yield the desired results.

The Tanstack Query 5 documentation mentions you can pass options as a Svelte Store to make it reactive.
While not very ergonomic, it is workable.

Alternatives like Svelte 4 reactive statements or re-creating the query in a $effect won't work because svelte's context becomes unavailable after the component initialization is complete. (this accidentally did work in early versions of svelte 5)

To make it a bit more ergnomic you can simplify the creation of a reactive store from your runes with a function like:

const toReadable = (cb) => readable(cb(), set => $effect.pre(() => set(cb())));

and then do something like:

let dependency = $state("something");
const query = createQuery(toReadable(() => ({
  queryKey: ["Resource", dependency],
  queryFn: () => getSomething(dependency)
})));

However, note that the above example is for createQuery.
For createInfiniteQuery, it appears to be a bit trickier. You might expect something like this to work:

let dependencyVariable = writable("whatever");
let query = createInfiniteQuery(
  derived(dependencyVariable, ($dependencyVariable) => ({
    queryKey: ["Resource", $dependencyVariable],
    queryFn: async ({ pageParam }) => {
      // Implementation omitted for brevity
    },
    getNextPageParam(lastPage) {
      // Implementation omitted for brevity
    },
    initialPageParam: null as string | null | undefined,
    initialDataUpdatedAt: () => Date.now(),
  }))
);

However, this gives me a lot of type errors, suggesting it is not the way to do it.


An additional example in the documentation specifically demonstrating how to use createInfiniteQuery reactively would be very helpful.

@frederikhors
Copy link
Contributor

Can you please create a stackblitz reproduction?

@SaintPepsi
Copy link

SaintPepsi commented May 21, 2024

I'm using Svelte 5 and needed to derive one of the variables in my createQuery. To achieve this, I tried wrapping createQuery in $derived. However, I encountered an error because createQuery uses getContext internally, and getContext can only be called on mount. Wrapping each use of the component with a derived query in {#key} was not a desirable solution.

The example provided by @nicksulkers wasn't working for me, as it resulted in the error:
"$effect() can only be used as an expression statement."

To solve this, I created an abstraction using a readable and queryClient.ensureQueryData. This approach functions similarly but doesn't require a context, so it avoids breaking in Svelte 5's runes and re-rendering. The downside is that I no longer have state data, just the data I want or null. Given my limited coding time outside of work, this was the best solution I could come up with.

Here's the implementation:

import queryClient from "$lib/utilities/tanstack/queryClient";
import type { DefaultError, EnsureQueryDataOptions, QueryKey } from "@tanstack/svelte-query";
import { readable, type Readable } from "svelte/store";

export const ensureQuery = <TQueryFnData, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: EnsureQueryDataOptions<TQueryFnData, TError, TData, TQueryKey>): Readable<TData | null> => {
  return readable<TData | null>(null, (set) => {
    queryClient.ensureQueryData(options).then(set);
  });
};

I then use it like this:

const ensuredQuery = $derived(
  ensureQuery({
    queryKey: ["Resource", dependency],
    queryFn: () => getSomething(dependency),
  })
);

I wish I know of a way to just do this:

const ensuredQuery = $ensureQuery({
  queryKey: ["Resource", dependency],
  queryFn: () => getSomething(dependency),
});

@nicksulkers
Copy link

nicksulkers commented May 21, 2024

@SaintPepsi here's the function as I'm actually using it:

utils.svelte.ts

export const reactiveQueryArgs = <T>(cb: () => T) => {
	const store = writable<T>();
	$effect.pre(() => {
		store.set(cb());
	});
	return store;
};

Usage could then be something like:

+page.svelte

let {search}: {search: string} = $props();

const query = createQuery(reactiveQueryArgs(() => ({
	queryKey: ["users", `search:${search}`],
	queryFn: () => fetch(`/users?search=${encodeURIComponent(search)}`)
})));

@SaintPepsi
Copy link

@nicksulkers is reactiveQueryArgs in a .svelte.ts file?

@SaintPepsi
Copy link

I was just reading through the source code and realised I can also just pass queryClient as second parameter to createQuery

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants