Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

In the Remote Function docs, it says:

Queries are cached while they’re on the page, meaning getPosts() === getPosts(). This means you don’t need a reference like const posts = getPosts() in order to update the query.

WTF does "while they're on the page" mean? What does "cached" (an overloaded term) mean?

My working interpretation of what was meant is:
Queries run once per navigation. Re-running the same query returns the exact same data reference (so getPosts() === getPosts() is true). Because the query value is stable, you don’t need to store it in a local reference (for example const posts = getPosts()) in order to update the query.

Is my interpretation correct? If it's cached (in memory?), what busts the cache? Navigation? 5 second timer?

const foo = { id: 1 };
getPost(foo) === getPost(foo);  // should this be true? why?

getPost({ id: 2 }) === getPost({ id: 2 }) // should this be true? why?
You must be logged in to vote

Replies: 3 comments · 1 reply

Comment options

getPost(foo).refresh()

You must be logged in to vote
0 replies
Comment options

Queries are cached while they’re on the page, meaning getPosts() === getPosts(). This means you don’t need a reference like const posts = getPosts() in order to update the query.

I have to agree that as I read this, I see the words, but I don't understand at all. What is helpful is concrete examples (and counter-examples), and breaking down step by step what is happening in each case. Summarizing key take-aways and highlighting possible misconceptions is also helpful.

You must be logged in to vote
0 replies
Comment options

I agree, the docs could use some work in general.

Some more in depth explanations from what I know in case that helps:

"same reference"

Parameters passed to remote functions are stringified (think of JSON.stringify, but actually, sveltekit uses devalue , so any transport hooks you've defined in hooks.ts will also be taken into account)

This string key is used to look up queries in the cache.

// same object reference of foo is not relevant here, but they will both look up the same JSON.stringify key of '{"id":1}'
const foo = { id: 1 };
getPost(foo) === getPost(foo);  

// this is true, as both times JSON.stringify({ id: 2 })  will look up the same string key of '{"id":2}'
getPost({ id: 2 }) === getPost({ id: 2 }) 

One thing to note here though, is that this also means that order of items is important! Which is quite a bummer and should either be addressed by the implementation, or noted in the docs.

// this will be false and will lead to another network request, as one will use the key  '{"a":1,"b":2}' and the other will use '{"b":2,"a":1}'
getPost({ a: 1, b: 2 }) === getPost({ b: 2, a: 1 })

"while they're on the page"

Its actually pretty simple if you're familiar with the createSubscriber of svelte. TL;DR: Svelte knows what state is being tracked.

let cachedValue = null;

const subscribe = createSubscriber(() => {
  // called one time when getPosts is called in a "tracked" context.
  return () => {
    // called when no more subscribers exist from those "tracked" environments.
  cachedValue = null; // invalidates cache
  }
});

async function getPosts() {
  subscribe();
  if (!cachedValue)  cachedValue = await fetch(...);
  return cachedValue;
}

This is just a very contrived example, it gets a bit more complicated if there are parameters involved, but I hope you get the idea.

So by "navigating away" you are destroying all svelte components that have initially called the query, which in turn unsubscribes all the signals depending on it.

Another note here: This only works if the query is actually called in a tracked context.

<script>
  const posts = await getPosts() // this is untracked!
  const posts = $derived(await getPosts()) // this is tracked

  function example() {
    const posts = await getPosts() // this is untracked (unless, example itself is called inside a $derived())
  }
</script>

{await getPosts()} This is tracked

In the untracked cases, it will invalidate the cache immediately, unless there are active subscribers to the cache that are active at the moment.

So if you care about performance, don't forget $derived() in the script tag (which you should be doing for any async work anyways to allow parallel loading)

You must be logged in to vote
1 reply
@kwangure
Comment options

This matched my expectations from testing but the note on untracked contexts was new. Well explained!

The other documentable case is:

const a = await getPost({ id: 2 });
await getPost({ id: 3 });
await getPost({ id: 2 }) === a; 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
4 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.