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

useFetch & useAsyncData reactive key #21532

Open
c-schwan opened this issue Jun 12, 2023 · 10 comments
Open

useFetch & useAsyncData reactive key #21532

c-schwan opened this issue Jun 12, 2023 · 10 comments

Comments

@c-schwan
Copy link
Contributor

hi @pi0, @danielroe

is it possible to pass the "key" on calling refresh/execute or making the key computed in options?

In my case i got a computed header with Authorization and custom key

const headers = computed(() => token.value ? { Authorization: Bearer ${token.value} } : undefined)
const key = hash([opts.baseURL, request, unref(opts.params || opts.query), unref(headers)])

so the fetchOptions are reactive and are watched by default. If the token changed on client the initialized key isn't changed.

@MfeaR
Copy link

MfeaR commented Sep 14, 2023

I'm have the same problem, can't caching result data at pagination

const route = useRoute();

const { data: users } = await useAsyncData(
  `users_page_${route.query.page}`,
  () =>
    axios
      .get("/api/users", {
        params: { page: route.query.page },
      })
      .then((res) => res.data),
  { watch: () => route.query.page }
);

@94726
Copy link
Contributor

94726 commented Sep 14, 2023

#22348 would probably also be solvable by this.

@connerblanton
Copy link
Contributor

+1 for this. Ran into an issue with this when using the new getCachedData as seen here - https://stackblitz.com/edit/github-wyzfak?file=pages%2Findex.vue

Using the getCachedData with query params can produce incorrect results when navigating back to a page

@Amar-Gill
Copy link

Amar-Gill commented Dec 2, 2023

+1 I have same issue.

My Situation

I am reactively changing a ref() in my top level <Nav> component. That ref is used in my useFetch() call as a reactive query param:

query: {
  searchParam: someReactiveRef,
}

The resulting data from useFetch is used to render a list of links that correspond to the specific query param. As the ref() changes, the data is re-fetched as required and I can reactively render the correct data in the list. This is good. ✔️

What's not working

Since the key is not being updated beyond the first run of useFetch, any use of useNuxtData(key) and refreshNuxtData(key) elsewhere in the app (typically inside a <NuxtPage />) will not get / refresh the data, even though it is present inside the <Nav /> component.

Specifically, the key is being derived from page route params and used in the API url query params.

Why this matters

Let's say inside a <NuxtPage /> I make a mutation with a call to POST /api/resource. On success, I need to call refreshNuxtData(key) so the change is reflected in the top-level <Nav /> component.

Reproduction

Here is a focused reproduction of what my app is doing: https://stackblitz.com/edit/nuxt-starter-buzcel?file=composables%2FuseResource.ts,app.vue,pages%2Fa.vue

Notes:

  • the returned data is re-fetched and renders correctly inside app.vue as you select different options from the <select> element (using a server generated timestamp to make it obvious)
  • the keyed state from useAsyncData never changes beyond initial useFetch call (see screenshot of Nuxt devtools)
  • as a consequence, hitting the refresh button does not update the data

Screenshot 2023-12-02 at 1 25 27 PM

  • notes on the screenshot:
    • key is resource-undefined because on page load the selected value of <select> element is undefined
    • was hoping to see the following keys resource-A, resource-B, resource-C so I can leverage the keys else where in app

Workaround

Wrapping the useFetch call in a watchEffect will enable the desired behaviour but seems boilerplate-y. If you want to access the pending or error values also, it adds to the boilerplate.

const { data: resource } = ref(null);

watchEffect(async () => {
  const { data } = useFetch('/api/resource', { key: someRef.value })
  resource.value = data.value
})

@RifatMahmudno-1
Copy link

Check this code https://stackblitz.com/edit/nuxt-starter-z3fdxq
Check utils/customFetch.js file
I wrote that function which solves reactive key issue and this problem #24332
The code is different from the code of useFetch but you will get the idea.

@danielroe
Copy link
Member

If this is an issue of interest to you, we'd love to hear some more use cases for where the current automatic reactivity in useFetch doesn't work (or manual refresh of useAsyncData).

@Yves852
Copy link

Yves852 commented Apr 18, 2024

On a project with Nuxt 3.11.2.

Changing calls from $fetch to useFetch I bumped into this limitation for additive pagination.

Initial solution ('immediate' to true or false doesn't change the result)

// Params not reactive
const { pending, refresh } = await useFetch(`/api/articles`, {
  method: 'POST',
  body: {
      pageNum: pageNum.value, // Remains to 1
      limit: 4,
    },
    onResponse({ response }) {
      if (response._data?.data?.length) {
        posts.value = [...posts.value, ...response._data.data]
        metaFilterCount.value = response._data?.meta?.filter_count
      }
    },
    onResponseError({ response }) {
      // Handle the response errors
      console.error(response)
    },
})

function nextPage() {
  pageNum.value++
  refresh()
}

I tried to replace it with Amar-Gill's workaround but in my case the pageNum.value was inconsistant.

Replacing useFetch with useAsyncData + $fetch did solved my problem.
Note that I had to use immediate: false, otherwise reloading the page (articles fetched during SSR) flush the array on client side and prevented further requests.

// This worked
const { pending, execute } = await useAsyncData('scroller',
  () => $fetch('/api/articles, {
    method: 'POST',
    body: {
      pageNum: pageNum.value,
      limit: 4,
    },
    onResponse({ response }) {
      if (response._data?.data?.length) {
        posts.value = [...posts.value, ...response._data.data]
        metaFilterCount.value = response._data?.meta?.filter_count
      }
    },
    onResponseError({ response }) {
      // Handle the response errors
      console.error(response)
    },
  }), 
  {
    immediate: false, // Needed this to works when the page is reloaded.
  },
)

// Load first articles
execute()

function nextPage() {
  pageNum.value++
  execute()
}

@RifatMahmudno-1
Copy link

RifatMahmudno-1 commented Apr 18, 2024

@Yves852 I faced similar problems. Maybe I failed to describe the problem properly to the devs in another issue. So, I wrote custom useFetch (named cFetch) and useAsyncData (named cAsyncData) function for my project. https://pastebin.com/X4N2eMNB
I've added some additional options, removed some options. There might be some bugs but I haven't faced any. Query, body and keys everything can be reactive. Take a look at the code. Maybe you'll get some idea.

Hope the devs will fix this problem in the upcoming update.

@Yves852
Copy link

Yves852 commented Apr 18, 2024

@RifatMahmudno-1 I don't know I stayed at the surface to be honnest, exploring what I can do from available tools from Nuxt and finally solved the problem for my case. I'm not brave enough yet to explore how Nuxt or Vuejs works.

I also note that they labelled it as "enhancement" therefore not a bug but a expected behavior. Also $fetch and useAsyncData can (for now) be used as workaround (Amar-Gill also suggested to wrap with watchEffect) so no emergency to fix.

I participated here because some other folks could lurk and appreciate or not that workaround.

@dennisadriaans
Copy link

I would love to use route.fullPath as the key for the useFetch method.

It's also kind of strange that watch: [route.fullPath] doesn't work either. You'd say it's a reactive property since it does update in the template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants