T
TanStack•3y ago
extended-salmon

Use derived stores with svelte-query 5

In the svelte-query 5 docs, it says you can pass a writable store for queryOptions like so:
const queryOptions = writable({
queryKey: ['refetch'],
queryFn: async () => await fetch(endpoint).then((r) => r.json()),
refetchInterval: 1000,
})
const query = createQuery(queryOptions)
const queryOptions = writable({
queryKey: ['refetch'],
queryFn: async () => await fetch(endpoint).then((r) => r.json()),
refetchInterval: 1000,
})
const query = createQuery(queryOptions)
I tested and it works great. However, I then tried passing a derived store, and get an error that it can't read the data from the derived store (it says my queryKey and queryFn are empty, even though if I subscribe to the derived store I can see they are populated). Does svelte-query 5 not support derived stores for query options?
36 Replies
ambitious-aqua
ambitious-aqua•3y ago
Can you provide an example? Also, shouldn't the queryOptions store be accessed as $queryOptions?
extended-salmon
extended-salmonOP•3y ago
Yes I will provide an example. In the docs online, it does not show the queryOptions being passed with a $ Hm, trying to put together an example in Codesandbox but I can't even get a basic QueryClientProvider working there, it is throwing an App constructor error? https://codesandbox.io/s/weathered-feather-8v8nvf?file=/App.svelte @Raytuzio can you let me know what I'm doing wrong here?
ambitious-aqua
ambitious-aqua•3y ago
Svelte stores need to be accessed with $ - the docs haven't been updated yet to demonstrate how to use this feature, but it's the same as the rest of svelte. That doesn't look like how you build a vite app with svelte. How did you generate this template?
ambitious-aqua
ambitious-aqua•3y ago
GitHub
query/examples/svelte/simple at main · 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. - query/examples/svelte/simple at main ·...
extended-salmon
extended-salmonOP•3y ago
This is a typical Codesandbox svelte template that works perfectly fine until svelte-query 5 is introduced...
extended-salmon
extended-salmonOP•3y ago
No, in svelte-query 5 you do not pass the store with a $ sign, the type for CreateBaseQuery options explicilty states that you can pass it a Writable, which would be a store that hasn't been unwrapped. Hence the ability to get reactivity. Which means the docs listed here are correct https://tanstack.com/query/v5/docs/svelte/reactivity CreateBaseQuery options type: https://github.com/TanStack/query/blob/alpha/packages/svelte-query/src/types.ts#L19-L27
Reactivity | TanStack Query Docs
Svelte uses a compiler to build your code which optimises rendering. By default, variables will run once, unless they are referenced in your markup. To be able to react to changes in options you need to use stores. In the below example, the refetchInterval option is set from the variable intervalMs, which is edited by the input field. However,...
GitHub
query/packages/svelte-query/src/types.ts at alpha · 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. - query/packages/svelte-query/src/types....
extended-salmon
extended-salmonOP•3y ago
In CreateBaseQuery you can see that it expects options to either be a writable or it converts it to a writable https://github.com/TanStack/query/blob/alpha/packages/svelte-query/src/createBaseQuery.ts#L27
GitHub
query/packages/svelte-query/src/createBaseQuery.ts at alpha · TanSt...
🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query. - query/packages/svelte-query/src/create...
ambitious-aqua
ambitious-aqua•3y ago
Oh I see, sorry, I haven't used it myself. So it works with svelte-query v4?
extended-salmon
extended-salmonOP•3y ago
Nah, it doesnt work with v4 either. And I can't load v4 in a svelte repl either, I get a "process is not defined" error. Trying to figure out best way to get you some reproducible code...
extended-salmon
extended-salmonOP•3y ago
Ok, I've got it working @Raytuzio Here is example 1, using svelte-query v5 with a writable store for the options like the documentation says: https://svelte.dev/repl/7d299dce4d9a4b6ba590d6707f383d62?version=4.0.2 And that works as expected!
extended-salmon
extended-salmonOP•3y ago
Now here is another version of that example where I try to use a derived store for queryOptions. It does not work at all - svelte query doesn't try to fetch anything. But I can see by logging in the console that my derived store queryOptions looks just like my options did in example 1 https://svelte.dev/repl/ee90c13684af4672a19661e9e9021da3?version=4.0.2
ambitious-aqua
ambitious-aqua•3y ago
Ahh I see - I love this example! You're right, it doesn't even attempt to fetch I'll have a look into it! I'd also appreciate it if you could create an issue on the github repo here: https://github.com/TanStack/query/issues
extended-salmon
extended-salmonOP•3y ago
GitHub
[Alpha] svelte-query v5 doesn't accept derived stores for options ·...
Describe the bug In the documentation for svelte-query v5, it is suggested that you can provide stores for queryOptions in order to make reactive queries: https://tanstack.com/query/v5/docs/svelte/...
ambitious-aqua
ambitious-aqua•3y ago
Hi @speros , just want to check that everything works with the new update?
extended-salmon
extended-salmonOP•3y ago
Yes it works! Awesome, thank you!
ambitious-aqua
ambitious-aqua•3y ago
No worries! I've definitely under-utilised derived stores in my own projects but they seem incredibly useful @speros I identified another bug now that the queries can be truly reactive - if you were using staleTime and changes didn't react properly before, they should work now: https://github.com/TanStack/query/pull/5682 Also if you don't need to access your derived store anywhere else, you can just do this:
const query = createQuery(
derived(store, person => ({
queryKey: ['myquery', person],
queryFn: async () => await fetch(`https://swapi.dev/api/people/${person}`).then((r) => r.json()),
}))
);
const query = createQuery(
derived(store, person => ({
queryKey: ['myquery', person],
queryFn: async () => await fetch(`https://swapi.dev/api/people/${person}`).then((r) => r.json()),
}))
);
extended-salmon
extended-salmonOP•3y ago
Awesome! Do all these subscriptions created internally by createQuery get cleaned up when that query produced is no longer being subscribed to? Might be something worth double checking
ambitious-aqua
ambitious-aqua•3y ago
Good point, I will check It should be fine based on the svelte docs 🙂
genetic-orange
genetic-orange•3y ago
this is probably not possible in v4 ?
ambitious-aqua
ambitious-aqua•3y ago
Sorry, it's not possible - you can achieve something similar (but not as good) with these instructions: https://tanstack.com/query/v4/docs/svelte/reactivity
Reactivity | TanStack Query Docs
Svelte uses a compiler to build your code which optimises rendering. By default, variables will run once, unless they are referenced in your markup. To make a different variable or function reactive, you need to use reactive declarations. This also applies to Svelte Query. In the below example, the refetchInterval option is set from the variab...
ambitious-aqua
ambitious-aqua•3y ago
Or just use v5 alpha! 🙂
robust-apricot
robust-apricot•3y ago
Guys @Raytuzio @speros when using derived with createQuery, there is chance, that you need import some writable store from .svelte to .ts file? Haven't you had any problems with TS doing so? Im having hard time with Module '"*.svelte"' has no exported member when importing from .svelte to .ts file. Maybe you have any workarounds for this?
ambitious-aqua
ambitious-aqua•3y ago
@kaszakukus I'm not quite sure what you mean - you shouldn't need to do that. Can you give an example?
robust-apricot
robust-apricot•3y ago
I have this in createAppQuery.ts
export function createAppQuery(options: CreateAppQueryOptions) {
return createQuery(
derived(dataRefetchIntervalS, ($dataRefetchIntervalS) => ({
refetchInterval: $dataRefetchIntervalS,
queryKey: options.queryKey,
queryFn: options.queryFn
}))
);
}
export function createAppQuery(options: CreateAppQueryOptions) {
return createQuery(
derived(dataRefetchIntervalS, ($dataRefetchIntervalS) => ({
refetchInterval: $dataRefetchIntervalS,
queryKey: options.queryKey,
queryFn: options.queryFn
}))
);
}
and dataRefetchIntervalS is store imported from <script context="module"></script> .svelte file because dataRefetchIntervalS is imported from .svelte to .ts file Im getting: error TS2614: Module '".svelte"' has no exported member 'dataRefetchIntervalS'. Did you mean to use 'import dataRefetchIntervalS from ".svelte"' instead? @Raytuzio thought its very common case. I am missing something?
ambitious-aqua
ambitious-aqua•3y ago
Does dataRefetchIntervalS need to be in a svelte file? Is it meant to represent a sharable store or a store specific to that svelte file?
robust-apricot
robust-apricot•3y ago
Basically its kinda sharable store - needed while creating query by createQuery, but its also connected with .svelte file which holds logic and UI to enable setting interval. I think the best workaround is to keep my function createAppQuery in .svelte file inside context="module" script, or as you suggest to move writable store to some .ts file
extended-salmon
extended-salmonOP•3y ago
Yes, I'm not super familiar with how svelte modules work but I don't believe there is any support for importing from them into TS files. Normally I would export and shared store from a TS file
sensitive-blue
sensitive-blue•3y ago
unfortunately types don't seem to fully work at the moment when using derived stores in queries/mutations: https://discord.com/channels/719702312431386674/1136157186796556438
sunny-green
sunny-green•3y ago
Hey @Raytuzio! I noticed that if my derived store returns the the same queryKey, but updated the queryFn, the underlying queryFn is never changed. Is that intended? If so, should I be including the parameter that my derived store is using in the key? Ah I see. Is there a recommended way to share CreateQueryResult objects between components? For example, I have a global query that's being executed in a parent component, and I want to subscribe to that same query from a child component. Ohhhh I think it’s because I was expecting that changing the query function would automatically invalidate and replace the query data too… is there any way to return an option from the derived update which would clear the query? Or I guess I would have to trigger it manually for each query?
ambitious-aqua
ambitious-aqua•3y ago
Hi @ap , that sort of behaviour doesn't sound like it should work - it won't try to refetch data if the same query key is used (this isn't a svelte-specific thing). Do you want to share your code so we can help find a solution?
sunny-green
sunny-green•3y ago
Sure here's the gist of it --
// In +layout.svelte:
const username = writable<string | undefined>(undefined);

setTimeout(() => username.set("alice"),5000);

const queryClient = new QueryClient(...);

const userQuery = createQuery(derived(username, ($username) => {
return {
queryKey: ["user","info"]
queryFn: $username ? someAsyncPromise() : Promise.resolve({})
)
}, queryClient);


// In Child.svelte:
const userQuery = createQuery({queryKey: ["user", "info"]});

$: console.log("Child", $userQuery.status, $userQuery.data)
// In +layout.svelte:
const username = writable<string | undefined>(undefined);

setTimeout(() => username.set("alice"),5000);

const queryClient = new QueryClient(...);

const userQuery = createQuery(derived(username, ($username) => {
return {
queryKey: ["user","info"]
queryFn: $username ? someAsyncPromise() : Promise.resolve({})
)
}, queryClient);


// In Child.svelte:
const userQuery = createQuery({queryKey: ["user", "info"]});

$: console.log("Child", $userQuery.status, $userQuery.data)
Basically, in the Child component, it seems to me that I'm not fetching the query correctly -- if the query resolves in the parent immediately, the child component never sees the {} resolve, and when the username changes, the Child component never sees the updated query, as the updated queryFn is never actually run. I'm sure I'm doing something wrong as I've never used react-query, or svelte-query before, but would be helpful to know exactly what (only have a faint idea) I mean -- I can see how the "a solution" would be to use a queryKey of ["user", "info", $username], and then use that in the Child component query as well, but I'm not sure if that is the "correct solution"
ambitious-aqua
ambitious-aqua•3y ago
If you change your queryKey to queryKey: ["user","info", $username] that would work for +layout.svelte. Is there a reason why username needs to start out undefined? I have to say I've never seen a conditional queryFn like that - if you're using SSR you might want to use a +layout.ts file then throw an error if username is undefined? However I don't know what your app looks like beyond this snippet.
sunny-green
sunny-green•3y ago
I'm basically using undefined as equivalent to a logged-out view in my app, so an empty object for user info is handled during rendering certain information panels. Yeah -- I guessed as much regarding the query key and so on. Makes sense to me why that would be the preferred approach. Just wrapping my head around it all. Tysm!
ambitious-aqua
ambitious-aqua•3y ago
No worries! Any time you expect a query to return data from a different endpoint/resource/whatever, it needs its own key
sunny-green
sunny-green•3y ago
Second issue: If I create the query, but don't use the returned store in the +layout.svelte, then the query never seems to resolve in the Child component. If I add a reader to the original query in layout, such as console.log($query.status), then it resolves. I can guess why this is, but what's the suggested workaround? Are queries meant to be kept in a global js/ts file separate from the component?
ambitious-aqua
ambitious-aqua•3y ago
If you don't need to use the user query in your +layout.svelte, you can remove it from there! You can just use the query directly in the component you actually want it in, such as your Child.svelte component. I would've expected it to still resolve despite this, but I don't know the full context of your app.

Did you find this page helpful?