qwik icon indicating copy to clipboard operation
qwik copied to clipboard

[✨] Allow loaders to be refetched programmatically and configure if they should refetch when an action is run

Open jweb89 opened this issue 2 years ago • 11 comments

Is your feature request related to a problem?

Yes. We have a pretty complex page which requires various api calls which we use a different loader for each. Not sure if this is feasible but it would be nice to

  1. Be able to opt in or opt out of the loader being rerun when an action is run. Some loaders we don't necessarily want to be rerun when any action is run. If there are many loaders the .json from the action can get pretty large and makes unnecessary api calls etc.
  2. Be able to programmatically refresh a particular loader. A use case for this would be that a user performs a particular interaction on the page and we only want to rerun a single loader

Describe the solution you'd like

export const getProductData = loader$(() => {
  return {
    product: {
      name: 'Qwik City',
      price: 100,
    },
  };
}, { strategy : 'manual' });

The strategies could be 'manual' or 'automatic' or maybe something more clear. But automatic would rerun when actions are called and manual would not. Default could be 'automatic' since that's the way it currently works now.

Additionally a loader can be rerun by calling getProductData.refresh()

I'm sure the semantics with the naming could be improved. But if these additions could be made to allow more control over the loaders that would make the qwik data fetching even more incredible

Describe alternatives you've considered

The alternative I guess is to leave it the way it is? I'm not sure if this is even feasible as I'm not super familiar with the lower level implementation of loaders and actions

Additional context

No response

jweb89 avatar Feb 15 '23 16:02 jweb89

I am new to Qwik and was looking for something like this to cache the loaders and not re-run them. Ideally, I prefer a similar solution to Next.js, where they parse the cache header to set this up automatically or give you a way to set up the timer on when to invalidate this cache.

Do you know if Qwik already parses the cache header for the loader$ to effectively deliver your 1st requirement?

adaliszk avatar Feb 15 '23 23:02 adaliszk

That is a fair point. However, it doesn't address the extra data that's going to be sent from the server back to the client even if the loader is cached on the server. Which is about 150KB in our case, nearly all of which doesn't need to be sent again.

Just tested the caching, doesn't seem like it's supported(the loaders and actions didn't work on stackblitz so you'll probably need to copy the code): https://stackblitz.com/edit/qwik-starter-ejmirx?file=src%2Froutes%2Findex.tsx,package.json

jweb89 avatar Feb 16 '23 00:02 jweb89

I was hoping that the cache header would trickle all that way so that re-fetching from the client side would only yield a no-change response. Thank you for testing it out!

It seems this is quite a missed opportunity.

adaliszk avatar Feb 16 '23 00:02 adaliszk

I was hoping that the cache header would trickle all that way so that re-fetching from the client side would only yield a no-change response. Thank you for testing it out!

It seems this is quite a missed opportunity.

The setup of loaders and actions doesn't really work that way. Once an action is finished running a q-data.json file is sent back to the browser which contains the result of the action and a rerun of all the loaders. This is what I'm referring to in ask 1

jweb89 avatar Feb 16 '23 00:02 jweb89

We want to solve this problem, but remember that actions can be

submits, ie full page reloads. In this case the loaders will need to run, just like any page refresh!

manucorporat avatar Feb 18 '23 10:02 manucorporat

We could do:

  • Some extra SPA only logic, could notify which loaders to run
  • Some server-side cache level for loaders

We will looking into both strategies! so please, keep the conversation going, i would love to understand better how you plan to use this APIs.

We will collect all this feedback and try to put it together!

manucorporat avatar Feb 18 '23 10:02 manucorporat

That’s a good point. I guess maybe it makes more sense to configure the “strategy” of the action instead of the loader and the strategy could either be rerun all the loaders with a full reload or only return the data from the action.

The specific problem I was hoping this would solve is handling authentication state. We are currently fetching the user in a loader and storing it in a context(so we can update the context when the user logs out, all from the client), but it would be ideal to treat the loader as the single source of truth.

The other way I could do it is to run an action to log out the user, which would rerun the user loader. This suffered from the first problem of sending a lot of data(about 150kb) and making many unnecessary requests due to the number of loaders we have.

That said, the programmatic loaders would pretty much solve this because we could log out and then refetch only that loader. But I could see use cases where it’s unnecessary for actions to cause full reloads. So adding a strategy to actions and programmatic loaders would allow the control needed to only reload what’s needed. Such as submitting a form as an action and then refetching one loader programmatically.

jweb89 avatar Feb 18 '23 13:02 jweb89

The ability to control when an action$ triggers some, none or all loaders would be helpful. For example, if you have a login form and the users login was successful, fire all loaders. But if the user login fails, fire none or only specific loaders.

tuurbo avatar Apr 29 '23 17:04 tuurbo

@manucorporat I'm using a server$ function, how can I trigger a full page reload with it?

juanpmarin avatar Aug 04 '23 02:08 juanpmarin

This issue has become quite a bottleneck for my app. Submitting a form in a nested route causes all routeLoaders in the parent layout files to run which in turn makes the response of my actions really slow.

DustinJSilk avatar Nov 21 '23 10:11 DustinJSilk

Bundled some duplicates. Generally speaking, the desired caching behavior seems to mostly be "forever". So if a routeloader ran once, cache it and never run it again while navigating.

Maybe this can be achieved in the serviceworker, adding SSR'd routerloaders into the browser cache?

wmertens avatar Feb 08 '24 09:02 wmertens