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

feat(nuxt)!: remove pending from data fetching composables #25864

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/2.guide/1.concepts/1.auto-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Nuxt auto-imports functions and composables to perform [data fetching](/docs/get
```vue
<script setup lang="ts">
/* useAsyncData() and $fetch() are auto-imported */
const { data, refresh, pending } = await useFetch('/api/hello')
const { data, refresh } = await useFetch('/api/hello')
</script>
```

Expand Down
6 changes: 2 additions & 4 deletions docs/3.api/2.composables/use-async-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ Within your pages, components, and plugins you can use useAsyncData to get acces

```vue [pages/index.vue]
<script setup lang="ts">
const { data, pending, error, refresh } = await useAsyncData(
const { data, error, refresh } = await useAsyncData(
'mountains',
() => $fetch('https://api.nuxtjs.dev/mountains')
)
</script>
```

::callout
`data`, `pending`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` is a plain function for refetching data.
`data`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` is a plain function for refetching data.
::

### Watch Params
Expand Down Expand Up @@ -88,7 +88,6 @@ Learn how to use `transform` and `getCachedData` to avoid superfluous calls to a
## Return Values

- `data`: the result of the asynchronous function that is passed in.
- `pending`: a boolean indicating whether the data is still being fetched.
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
- `error`: an error object if the data fetching failed.
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
Expand Down Expand Up @@ -127,7 +126,6 @@ type AsyncDataOptions<DataT> = {

type AsyncData<DataT, ErrorT> = {
data: Ref<DataT | null>
pending: Ref<boolean>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
error: Ref<ErrorT | null>
Expand Down
10 changes: 4 additions & 6 deletions docs/3.api/2.composables/use-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ It automatically generates a key based on URL and fetch options, provides type h

```vue [pages/modules.vue]
<script setup lang="ts">
const { data, pending, error, refresh } = await useFetch('/api/modules', {
const { data, error, refresh } = await useFetch('/api/modules', {
pick: ['title']
})
</script>
```

::callout
`data`, `pending`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` is a plain function for refetching data.
`data`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` is a plain function for refetching data.
::

Using the `query` option, you can add search parameters to your query. This option is extended from [unjs/ofetch](https://github.com/unjs/ofetch) and is using [unjs/ufo](https://github.com/unjs/ufo) to create the URL. Objects are automatically stringified.

```ts
const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('/api/modules', {
const { data, error, refresh } = await useFetch('/api/modules', {
query: { param1, param2: 'value2' }
})
```
Expand All @@ -43,7 +43,7 @@ The above example results in `https://api.nuxt.com/modules?param1=value1&param2=
You can also use [interceptors](https://github.com/unjs/ofetch#%EF%B8%8F-interceptors):

```ts
const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
const { data, error, refresh } = await useFetch('/api/auth/login', {
onRequest({ request, options }) {
// Set the request headers
options.headers = options.headers || {}
Expand Down Expand Up @@ -114,7 +114,6 @@ Learn how to use `transform` and `getCachedData` to avoid superfluous calls to a
## Return Values

- `data`: the result of the asynchronous function that is passed in.
- `pending`: a boolean indicating whether the data is still being fetched.
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
- `error`: an error object if the data fetching failed.
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
Expand Down Expand Up @@ -155,7 +154,6 @@ type UseFetchOptions<DataT> = {

type AsyncData<DataT, ErrorT> = {
data: Ref<DataT | null>
pending: Ref<boolean>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
error: Ref<ErrorT | null>
Expand Down
4 changes: 2 additions & 2 deletions docs/3.api/2.composables/use-lazy-async-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ By default, [`useAsyncData`](/docs/api/composables/use-async-data) blocks naviga
/* Navigation will occur before fetching is complete.
Handle pending and error states directly within your component's template
*/
const { pending, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
const { status, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))

watch(count, (newCount) => {
// Because count might start out null, you won't have access
Expand All @@ -35,7 +35,7 @@ watch(count, (newCount) => {

<template>
<div>
{{ pending ? 'Loading' : count }}
{{ status === 'pending' ? 'Loading' : count }}
</div>
</template>
```
Expand Down
4 changes: 2 additions & 2 deletions docs/3.api/2.composables/use-lazy-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ By default, [`useFetch`](/docs/api/composables/use-fetch) blocks navigation unti
/* Navigation will occur before fetching is complete.
Handle pending and error states directly within your component's template
*/
const { pending, data: posts } = await useLazyFetch('/api/posts')
const { status, data: posts } = await useLazyFetch('/api/posts')
watch(posts, (newPosts) => {
// Because posts might start out null, you won't have access
// to its contents immediately, but you can watch it.
})
</script>

<template>
<div v-if="pending">
<div v-if="status === 'pending'">
Loading ...
</div>
<div v-else>
Expand Down
2 changes: 1 addition & 1 deletion docs/3.api/3.utils/clear-nuxt-data.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 'clearNuxtData'
description: Delete cached data, error status and pending promises of useAsyncData and useFetch.
description: Delete cached data and error status promises of useAsyncData and useFetch.
DamianGlowala marked this conversation as resolved.
Show resolved Hide resolved
links:
- label: Source
icon: i-simple-icons-github
Expand Down
4 changes: 2 additions & 2 deletions docs/3.api/3.utils/refresh-nuxt-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ This example below refreshes only data where the key matches to `count`.

```vue [pages/some-page.vue]
<script setup lang="ts">
const { pending, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
const { status, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
const refresh = () => refreshNuxtData('count')
</script>

<template>
<div>
{{ pending ? 'Loading' : count }}
{{ status === 'pending' ? 'Loading' : count }}
</div>
<button @click="refresh">Refresh</button>
</template>
Expand Down
7 changes: 0 additions & 7 deletions packages/nuxt/src/app/composables/asyncData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ export interface AsyncDataExecuteOptions {

export interface _AsyncData<DataT, ErrorT> {
data: Ref<DataT>
pending: Ref<boolean>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
error: Ref<ErrorT | null>
Expand Down Expand Up @@ -249,7 +248,6 @@ export function useAsyncData<

nuxtApp._asyncData[key] = {
data: _ref(options.getCachedData!(key) ?? options.default!()),
pending: ref(!hasCachedData()),
error: toRef(nuxtApp.payload._errors, key),
status: ref('idle')
}
Expand All @@ -270,7 +268,6 @@ export function useAsyncData<
if ((opts._initial || (nuxtApp.isHydrating && opts._initial !== false)) && hasCachedData()) {
return Promise.resolve(options.getCachedData!(key))
}
asyncData.pending.value = true
asyncData.status.value = 'pending'
// TODO: Cancel previous promise
const promise = new Promise<ResT>(
Expand Down Expand Up @@ -310,8 +307,6 @@ export function useAsyncData<
.finally(() => {
if ((promise as any).cancelled) { return }

asyncData.pending.value = false

delete nuxtApp._asyncDataPromises[key]
})
nuxtApp._asyncDataPromises[key] = promise
Expand Down Expand Up @@ -354,7 +349,6 @@ export function useAsyncData<

if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || hasCachedData())) {
// 1. Hydration (server: true): no fetch
asyncData.pending.value = false
asyncData.status.value = asyncData.error.value ? 'error' : 'success'
} else if (instance && ((nuxtApp.payload.serverRendered && nuxtApp.isHydrating) || options.lazy) && options.immediate) {
// 2. Initial load (server: false): fetch on mounted
Expand Down Expand Up @@ -493,7 +487,6 @@ export function clearNuxtData (keys?: string | string[] | ((key: string) => bool
if (nuxtApp._asyncData[key]) {
nuxtApp._asyncData[key]!.data.value = undefined
nuxtApp._asyncData[key]!.error.value = null
nuxtApp._asyncData[key]!.pending.value = false
nuxtApp._asyncData[key]!.status.value = 'idle'
}
if (key in nuxtApp._asyncDataPromises) {
Expand Down
1 change: 0 additions & 1 deletion packages/nuxt/src/app/nuxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ interface _NuxtApp {
/** @internal */
_asyncData: Record<string, {
data: Ref<any>
pending: Ref<boolean>
error: Ref<Error | null>
status: Ref<AsyncDataRequestStatus>
} | undefined>
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/basic/pages/useAsyncData/nuxt-data.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
v-if="!pending"
v-if="status !== 'pending'"
v-text="'resolved:' + data.resolved"
/>
<div
Expand All @@ -11,5 +11,5 @@

<script setup>
useNuxtData('call')
const { data, pending } = await useAsyncData('call', () => Promise.resolve({ resolved: true }), { server: false })
const { data, status } = await useAsyncData('call', () => Promise.resolve({ resolved: true }), { server: false })
</script>
4 changes: 2 additions & 2 deletions test/fixtures/runtime-compiler/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ComponentDefinedInSetup = computed(() => h({
template: compTemplate.value
}) as Component)

const { data, pending } = await useAsyncData('templates', async () => {
const { data, status } = await useAsyncData('templates', async () => {
const [interactiveComponent, templateString] = await Promise.all([
$fetch('/api/full-component'),
$fetch('/api/template')
Expand Down Expand Up @@ -54,7 +54,7 @@ const Interactive = h({
>
{{ count }}
</button>
<template v-if="!pending">
<template v-if="status !== 'pending'">
<Name
id="name"
template="<div>I am the Name.ts component</div>"
Expand Down
13 changes: 4 additions & 9 deletions test/nuxt/composables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ describe('useAsyncData', () => {
expect(Object.keys(res)).toMatchInlineSnapshot(`
[
"data",
"pending",
"error",
"status",
"execute",
Expand All @@ -157,29 +156,25 @@ describe('useAsyncData', () => {
const immediate = await useAsyncData(() => Promise.resolve('test'))
expect(immediate.data.value).toBe('test')
expect(immediate.status.value).toBe('success')
expect(immediate.pending.value).toBe(false)

const nonimmediate = await useAsyncData(() => Promise.resolve('test'), { immediate: false })
expect(nonimmediate.data.value).toBe(null)
expect(nonimmediate.status.value).toBe('idle')
expect(nonimmediate.pending.value).toBe(true)
})

it('should capture errors', async () => {
const { data, error, status, pending } = await useAsyncData('error-test', () => Promise.reject(new Error('test')), { default: () => 'default' })
const { data, error, status } = await useAsyncData('error-test', () => Promise.reject(new Error('test')), { default: () => 'default' })
expect(data.value).toMatchInlineSnapshot('"default"')
expect(error.value).toMatchInlineSnapshot('[Error: test]')
expect(status.value).toBe('error')
expect(pending.value).toBe(false)
expect(useNuxtApp().payload._errors['error-test']).toMatchInlineSnapshot('[Error: test]')

// TODO: fix the below
// const { data: syncedData, error: syncedError, status: syncedStatus, pending: syncedPending } = await useAsyncData('error-test', () => ({}), { immediate: false })
// const { data: syncedData, error: syncedError, status: syncedStatus } = await useAsyncData('error-test', () => ({}), { immediate: false })

// expect(syncedData.value).toEqual(null)
// expect(syncedError.value).toEqual(error.value)
// expect(syncedStatus.value).toEqual('idle')
// expect(syncedPending.value).toEqual(true)
})

// https://github.com/nuxt/nuxt/issues/23411
Expand Down Expand Up @@ -233,9 +228,9 @@ describe('useAsyncData', () => {

it('should use default while pending', async () => {
const promise = useAsyncData(() => Promise.resolve('test'), { default: () => 'default' })
const { data, pending } = promise
const { data, status } = promise

expect(pending.value).toBe(true)
expect(status.value).toBe('pending')
expect(data.value).toMatchInlineSnapshot('"default"')

await promise
Expand Down