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

Opt out of load invalidation #6294

Closed
Rich-Harris opened this issue Aug 25, 2022 · 21 comments
Closed

Opt out of load invalidation #6294

Rich-Harris opened this issue Aug 25, 2022 · 21 comments
Labels
feature / enhancement New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. router
Milestone

Comments

@Rich-Harris
Copy link
Member

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:

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

@Rich-Harris Rich-Harris added this to the post-1.0 milestone Aug 26, 2022
@dummdidumm dummdidumm added feature / enhancement New feature or request router p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. labels Aug 26, 2022
@dummdidumm
Copy link
Member

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".

@stalkerg
Copy link
Contributor

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.

@stalkerg
Copy link
Contributor

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;
  ...

@Algoinde
Copy link
Contributor

Algoinde commented Jan 11, 2023

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.

@stalkerg
Copy link
Contributor

@Algoinde it's not a bad idea, but firstly team should get back to this issue.

@dummdidumm
Copy link
Member

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.

@Algoinde
Copy link
Contributor

Algoinde commented Jan 11, 2023

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).

@dummdidumm
Copy link
Member

So your proposal is something like event.untracked.url.. instead of untrack(() => event.url..)?

@Algoinde
Copy link
Contributor

Algoinde commented Jan 11, 2023

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.

@dummdidumm
Copy link
Member

#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.

@stalkerg
Copy link
Contributor

@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?

@dummdidumm
Copy link
Member

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.

@Algoinde
Copy link
Contributor

Algoinde commented Mar 13, 2023

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.

@dummdidumm
Copy link
Member

Totally valid, yes, this is more a QOL thing

@lukaszpolowczyk
Copy link
Contributor

Related? - Function isInvalidating(url) for load - #6495

@Tjomas
Copy link

Tjomas commented May 10, 2023

At the moment I am facing the same problem. A simple goto('....', { invalidateNothing:true}) or something similar would help me a lot.

@pabueco
Copy link

pabueco commented Jun 14, 2023

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; 

@stalkerg
Copy link
Contributor

@pabueco as I understand it's will be slower than my way, no?

@eltigerchino
Copy link
Member

eltigerchino commented Aug 19, 2023

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.

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'
  ]
});

@seanvelasco
Copy link

seanvelasco commented Nov 19, 2023

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)
}

@stalkerg
Copy link
Contributor

@seanvelasco at least before .url was a private.

dummdidumm added a commit that referenced this issue Dec 14, 2023
dummdidumm added a commit that referenced this issue Dec 14, 2023
@dummdidumm dummdidumm modified the milestones: soon, 2.0 Dec 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature / enhancement New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. router
Projects
None yet
Development

No branches or pull requests

9 participants