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): add clear function to async data #26259

Merged
merged 9 commits into from
Mar 17, 2024
1 change: 1 addition & 0 deletions docs/1.getting-started/6.data-fetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ Read more about `useAsyncData`.
- `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.
- `clear`: a function that can be used to set `data` to undefined, set `error` to `null`, set `pending` to `false`, set `status` to `idle`, and mark any currently pending requests as cancelled.
- `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
1 change: 1 addition & 0 deletions docs/3.api/2.composables/use-async-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ type AsyncData<DataT, ErrorT> = {
pending: Ref<boolean>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
clear: () => void
error: Ref<ErrorT | null>
status: Ref<AsyncDataRequestStatus>
};
Expand Down
1 change: 1 addition & 0 deletions docs/3.api/2.composables/use-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ type AsyncData<DataT, ErrorT> = {
pending: Ref<boolean>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
clear: () => void
error: Ref<ErrorT | null>
status: Ref<AsyncDataRequestStatus>
}
Expand Down
46 changes: 29 additions & 17 deletions packages/nuxt/src/app/composables/asyncData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface _AsyncData<DataT, ErrorT> {
pending: Ref<boolean>
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
clear: () => void
error: Ref<ErrorT | null>
status: Ref<AsyncDataRequestStatus>
}
Expand Down Expand Up @@ -221,8 +222,9 @@ export function useAsyncData<
if (value) { return value as Promise<ResT> }

const promise = nuxtApp.runWithContext(_handler)
nuxtApp.ssrContext!._sharedPrerenderCache!.set(key, promise)
return promise

nuxtApp.ssrContext!._sharedPrerenderCache!.set(key, promise)
return promise
}

// Used to get default values
Expand Down Expand Up @@ -322,6 +324,8 @@ export function useAsyncData<
return nuxtApp._asyncDataPromises[key]!
}

asyncData.clear = () => clearNuxtDataByKey(nuxtApp, key)

const initialFetch = () => asyncData.refresh({ _initial: true })

const fetchOnServer = options.server !== false && nuxtApp.payload.serverRendered
Expand Down Expand Up @@ -499,21 +503,29 @@ export function clearNuxtData (keys?: string | string[] | ((key: string) => bool
: toArray(keys)

for (const key of _keys) {
if (key in nuxtApp.payload.data) {
nuxtApp.payload.data[key] = undefined
}
if (key in nuxtApp.payload._errors) {
nuxtApp.payload._errors[key] = null
}
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) {
nuxtApp._asyncDataPromises[key] = undefined
}
clearNuxtDataByKey(nuxtApp, key)
}
}

function clearNuxtDataByKey (nuxtApp: NuxtApp, key: string): void {
if (key in nuxtApp.payload.data) {
nuxtApp.payload.data[key] = undefined
}

if (key in nuxtApp.payload._errors) {
nuxtApp.payload._errors[key] = null
}

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) {
(nuxtApp._asyncDataPromises[key] as any).cancelled = true
nuxtApp._asyncDataPromises[key] = undefined
}
}

Expand Down
13 changes: 13 additions & 0 deletions test/nuxt/composables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ describe('useAsyncData', () => {
"status",
"execute",
"refresh",
"clear",
]
`)
expect(res instanceof Promise).toBeTruthy()
Expand Down Expand Up @@ -200,6 +201,18 @@ describe('useAsyncData', () => {
expect(data.data.value).toMatchInlineSnapshot('"test"')
})

it('should be clearable', async () => {
const { data, error, pending, status, clear } = await useAsyncData(() => Promise.resolve('test'))
expect(data.value).toBe('test')

clear()

expect(data.value).toBeUndefined()
expect(error.value).toBeNull()
expect(pending.value).toBe(false)
expect(status.value).toBe('idle')
})

it('allows custom access to a cache', async () => {
const { data } = await useAsyncData(() => ({ val: true }), { getCachedData: () => ({ val: false }) })
expect(data.value).toMatchInlineSnapshot(`
Expand Down