kit
kit copied to clipboard
Opt out of `load` invalidation
Describe the problem
When running a load
function, SvelteKit notes what dependencies the function has...
-
url.pathname
,url.search
, etc -
params.x
,params.y
- resources loaded with
fetch
-
await parent()
...and re-runs the function when those things change. It's possible to force an invalidation with invalidate(resource)
(where the load
function called depends(resource)
) or with invalidate()
(which invalidates all load
functions), but it's not possible to opt out of invalidation, which is occasionally helpful:
- you want to log some values but not 'observe' them
- you want to implement more fine-grained invalidation, a la #5850
Describe the proposed solution
We could add an untrack
function to load
:
export function load({ params, untrack }) {
untrack(() => console.log(`this will not re-run when ${params.foo}` changes...`));
console.log(`...but it will re-run when ${params.bar} changes`);
}
Code executed synchronously inside the callback would not affect the function's dependencies.
Alternatives considered
The alternative is to require developers to be completely explicit about when load
should re-run, or to always re-run every load
function (which no-one wants).
Importance
nice to have
Additional Information
No response
Another idea brought up by @Conduitry is to have a separate object which contains the same parameters but in an untracked way. The disadvantage is that the API is somewhat clunky, the advantage is that you are not constrained to "access inside untrack
needs to be synchronously".
For my case, I must use the lang
parameter from the URL query to make a request for the current session (user), and it should be only once.
Just to be clear about post-1.0
tag - this is a hard stopper for me, and I can't use SveltKit on production without such a feature.
Verbose await parent()
from #6183 it's annoying, but we can leave with it but the current issue is much more severe.
In my situation, page load delay increased from 300ms to 600ms.
I found a way how I can avoid load invalidation with url
, it's a hack, but maybe it can help somebody:
let symbolKeyCache;
/** @type {import('./$types').LayoutServerLoad} */
export async function load({ request }) {
if (!symbolKeyCache) {
symbolKeyCache = Reflect.ownKeys(request).find(key => key.toString() === 'Symbol(state)');
}
const query = request[symbolKeyCache].url.searchParams;
...
Had a showerthought about a possible solution for this. I think the concept of "automatic invalidation" is inherently confusing - you did not do anything to cause that, it just happens inside the framework. Most of the time you'd rather conserve on requests rather than invalidate everything, therefore you'll want imperative control over what exactly you want to invalidate, and what you want to just simply reference.
My idea is event.track
- an object that includes all of the invalidating things like url, params and so on (even maybe moving depends
there), and if accessed via it in the style of event.track.url.searchParams
, will raise the invalidation flag. This way, you're very explicit about the behavior - and no need to opt out, you instead opt in.
I currently opt out via
const absolutelyNotSearchParams =
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(event.url), 'searchParams').get.call(event.url)
and it feels very cursed to do this. One thing I like about Svelte is I don't have to wrangle with it, it's permissive. Here I feel it turned out to be the reverse.
If the invalidation is preferred by default and you'd rather not change the entire API after 1.0, you can put the concept on its head and do event.untrack.url.searchParams
. Via intellisense, this will neatly provide the context to what is being tracked.
@Algoinde it's not a bad idea, but firstly team should get back to this issue.
Making this opt-in feels a little like React's useEffect
with an explicit dependency array. I think this would be cumbersome for the majority of users/use cases. For example if I depend on a route parameter on my load function I'd expect it to rerun when that parameter changes.
We already have explicit opt-in through depends
/invalidate
, opting out would give us the missing piece here.
Yeah, I think simply shadowing it behind an event prop would be a good way to gain the control in a non-confusing way (which I feel the proposed untrack function is).
So your proposal is something like event.untracked.url..
instead of untrack(() => event.url..)
?
Yeah, pretty much. Scatter it around the load
code, no need to wrap in anything every time - as long as you go through untracked
or notrack
or whatever the name should be, it's fine. And it only has properties in which getters are tracked by default.
#9390 would also be solves through this, though it got me thinking that we probably also need to enhance goto
for this. Right now there's a invalidateAll
option, and we probably also need a invalidate: [..]
option so that you can explicitly rerun a untracked load function.
@dummdidumm if you need a rerun function that uses untracked URLs you can do it by tracked part. The problem is only with functions which not include any fetch
or other tracked attributes, but in that case, why do you want to rerun it?
The use case would be "don't rerun this unless I explictly tell it to", which you can do with depends
and invalidate
- but there's no way to invalidate specific keys while using goto
, which I think would be needed as part of the whole picture for this feature.
In my case for these opt-outs I just await invalidate
the keys first before doing a goto
. Is that a wrong approach? Seems to work absolutely fine and is intuitive, too.
Totally valid, yes, this is more a QOL thing
Related? - Function isInvalidating(url) for load - https://github.com/sveltejs/kit/issues/6495
At the moment I am facing the same problem. A simple goto('....', { invalidateNothing:true}) or something similar would help me a lot.
In case someone else needs this now, you can use Object.getOwnPropertyDescriptor
to get object properties without triggering a the invalidation. Based on the solution from @Algoinde 🙌
const someUntrackedParam = Object.getOwnPropertyDescriptor(event.params, 'some-param')?.value;
@pabueco as I understand it's will be slower than my way, no?
The use case would be "don't rerun this unless I explictly tell it to", which you can do with
depends
andinvalidate
- but there's no way to invalidate specific keys while usinggoto
, which I think would be needed as part of the whole picture for this feature.
Would that look something like passing in valid invalidate
arguments into an array? (I'm assuming we'll await the invalidations with Promise.all
).
import { goto } from '$app/navigation';
goto('/', {
invalidate: [
'/foo',
new URL('/example'),
(url) => url.pathname === '/bar'
]
});
i'm not sure if this is on-topic, but this is how i got around load invalidation when my load function depends on search params:
export const load: LayoutServerLoad = async ({ request }) => {
const { searchParams } = new URL(request.url)
console.log(searchParams)
}
@seanvelasco at least before .url
was a private.