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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-use getCachedData when refreshing asyncData #24332

Open
3 of 4 tasks
manniL opened this issue Nov 16, 2023 · 13 comments 路 May be fixed by #25850
Open
3 of 4 tasks

Re-use getCachedData when refreshing asyncData #24332

manniL opened this issue Nov 16, 2023 · 13 comments 路 May be fixed by #25850

Comments

@manniL
Copy link
Member

manniL commented Nov 16, 2023

Describe the feature

Hey 馃憢馃徎

With the introduction of getCachedData, useFetch and useAsyncData have an elegant way to re-use existing data easily.

There is a downside though, as @broox mentioned in this YouTube comment:

When refreshing the data, either via calling refresh manually or implicitly using it through the watch property or by defining reactive parameters like query in the useFetch options, there is no option to re-use the cached data.

Reason behind it is that we only take cached data into account when nuxt is hydrating or on the initial request of the composable.

if ((opts._initial || (nuxt.isHydrating && opts._initial !== false)) && hasCachedData()) {
return Promise.resolve(options.getCachedData!(key))
}

Spontaneous idea to resolve this issue:

  1. Add a force parameter for refresh, so manual refresh() calls can bypass the getCachedData function if necessary. By default, this can be set to true if we want to avoid breaking changes.
  2. Set force to false by default for watch. This is a behavior change, but when implementing an own getCachedData function you can take this into account easily in there. Projects not using the getCachedData function should not be impacted.

More than happy for other ideas or feedback 鈽猴笍

Additional information

  • Would you be willing to help implement this feature?
  • Could this feature be implemented as a module?

Final checks

@broox
Copy link

broox commented Nov 16, 2023

in my opinion, it should be default behavior that all requests, even those made by reactive changes, use the cache if your definition of getCachedData() resolves cached data.

i.e. the definition of getCachedData() should be the "forcing" function to use the cache.

i understand that you're thinking about potential implications for folks that have already implemented this feature, but introducing a force parameter for something that you'd expect to be default behavior feels like a weird developer UX. just my thoughts!

@manniL
Copy link
Member Author

manniL commented Nov 16, 2023

i.e. the definition of getCachedData() should be the "forcing" function to use the cache.

I think that sounds fair, if the function is specified and returns data, it should be used instead of calling again by default.

But it'd be still nice to have a chance to bypass the function "on demand" (which would be equivalent to a hard reload or similar). I wonder if we need the force parameter for that though or if that could be implemented in userland (which I guess it could be easily. 馃

@broox
Copy link

broox commented Nov 16, 2023

ah, yes. maybe it would be best if reactive changes attempt to use the cache, but refresh() does not?

or, like you suggested, refresh() could use the cache unless the force parameter is passed into it? similar to tapping refresh vs shift+refresh in your browser.

@darioferderber
Copy link
Contributor

Hi!

The problem is that key is changed.
You can try setting your own key.

const { data } = await useFetch('https://icanhazdadjoke.com/', {
  key: 'myOwnKey',
  query: { page },
  getCachedData: (key) => nuxt.payload.data[key] || nuxt.static.data[key],
  headers: {
    Accept: 'application/json',
  },
});

Hope this helps! 馃憢

@manniL
Copy link
Member Author

manniL commented Nov 23, 2023

Hi!

The problem is that key is changed. You can try setting your own key.

const { data } = await useFetch('https://icanhazdadjoke.com/', {
  key: 'myOwnKey',
  query: { page },
  getCachedData: (key) => nuxt.payload.data[key] || nuxt.static.data[key],
  headers: {
    Accept: 'application/json',
  },
});

Hope this helps! 馃憢

I thought so too initially but it is not enough if e.g. query is reactive.

@darioferderber
Copy link
Contributor

I thought so too initially but it is not enough if e.g. query is reactive.

Do you mean like in this example?

@manniL
Copy link
Member Author

manniL commented Nov 24, 2023

As soon as you use a page parameter it works, but if you "re-use" a local useFetch call it is tricky

@darioferderber
Copy link
Contributor

darioferderber commented Nov 27, 2023

As soon as you use a page parameter it works, but if you "re-use" a local useFetch call it is tricky

Thanks, I get it now 馃檪

There's already an option in asyncData - _initial, I think it could help because it will return the cached data if you set it to true.

 if ((opts._initial || (nuxt.isHydrating && opts._initial !== false)) && hasCachedData()) { 
   return Promise.resolve(options.getCachedData!(key)) 
 } 

So this could be the way to "re-use" a local useFetch

<button @click="refresh({ _initial: true })">New Joke (refresh)</button>

Maybe it's a bit "hacky" but it should work 馃

@manniL
Copy link
Member Author

manniL commented Nov 27, 2023

@darioferderber _initial is an internal app not meant for usage though (hance the _ in the name 馃構) - so yes, that might work but better DX would be nicer.

@darioferderber
Copy link
Contributor

@darioferderber _initial is an internal app not meant for usage though (hance the _ in the name 馃構) - so yes, that might work but better DX would be nicer.

Yup, I know, but it could help @broox if he's still struggling with this issue 馃槃

I agree, there should be a better DX to deal with this, but basically it should work the same as _initial

@broox
Copy link

broox commented Nov 29, 2023

@darioferderber - i don't think the private _initial hack helps because i'm not calling refresh() manually.

the http request is automatically triggered when i change the reactive query parameters of useFetch(). in this case, i'd simply like the request to respect the cached data.

@manniL
Copy link
Member Author

manniL commented Nov 29, 2023

the http request is automatically triggered when i change the reactive query parameters of useFetch(). in this case, i'd simply like the request to respect the cached data.

Internally, a refresh is called IIRC. But that doesn't help because you can't pass the prop there indeed.

@AndersCV
Copy link

Is there any update on this one? I spent quite a while thinking it was my own bug trying figuring out why my getCachedData function was only being triggered on the initial requests and not re-used when my watch sources changes.

This would be a great addition and could save me a bunch of request with my paginated data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

Successfully merging a pull request may close this issue.

5 participants