feat(nuxt): respect defaults when clearing asyncData (#27295)

This commit is contained in:
Daniel Roe 2024-05-22 15:42:19 +01:00 committed by GitHub
parent 305c7348bd
commit 811bfc18a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 4 deletions

View File

@ -48,6 +48,7 @@ export default defineNuxtConfig({
// experimental: { // experimental: {
// sharedPrerenderData: false, // sharedPrerenderData: false,
// compileTemplate: true, // compileTemplate: true,
// resetAsyncDataToUndefined: true,
// templateUtils: true, // templateUtils: true,
// relativeWatchPaths: true, // relativeWatchPaths: true,
// defaults: { // defaults: {
@ -220,6 +221,32 @@ export default defineNuxtConfig({
Please report an issue if you are doing this, as we do not plan to keep this as configurable. Please report an issue if you are doing this, as we do not plan to keep this as configurable.
#### Respect defaults when clearing `data` in `useAsyncData` and `useFetch`
🚦 **Impact Level**: Minimal
##### What Changed
If you provide a custom `default` value for `useAsyncData`, this will now be used when calling `clear` or `clearNuxtData` and it will be reset to its default value rather than simply unset.
##### Reasons for Change
Often users set an appropriately empty value, such as an empty array, to avoid the need to check for `null`/`undefined` when iterating over it. This should be respected when resetting/clearing the data.
##### Migration Steps
If you encounter any issues you can revert back to the previous behavior, for now, with:
```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
experimental: {
resetAsyncDataToUndefined: true,
}
})
```
Please report an issue if you are doing so, as we do not plan to keep this as configurable.
#### Shallow Data Reactivity in `useAsyncData` and `useFetch` #### Shallow Data Reactivity in `useAsyncData` and `useFetch`
🚦 **Impact Level**: Minimal 🚦 **Impact Level**: Minimal

View File

@ -8,7 +8,7 @@ import { createError } from './error'
import { onNuxtReady } from './ready' import { onNuxtReady } from './ready'
// @ts-expect-error virtual file // @ts-expect-error virtual file
import { asyncDataDefaults } from '#build/nuxt.config.mjs' import { asyncDataDefaults, resetAsyncDataToUndefined } from '#build/nuxt.config.mjs'
// TODO: temporary module for backwards compatibility // TODO: temporary module for backwards compatibility
import type { DefaultAsyncDataErrorValue, DefaultAsyncDataValue } from '#app/defaults' import type { DefaultAsyncDataErrorValue, DefaultAsyncDataValue } from '#app/defaults'
@ -267,11 +267,15 @@ export function useAsyncData<
pending: ref(!hasCachedData()), pending: ref(!hasCachedData()),
error: toRef(nuxtApp.payload._errors, key), error: toRef(nuxtApp.payload._errors, key),
status: ref('idle'), status: ref('idle'),
_default: options.default!,
} }
} }
// TODO: Else, somehow check for conflicting keys with different defaults or fetcher // TODO: Else, somehow check for conflicting keys with different defaults or fetcher
const asyncData = { ...nuxtApp._asyncData[key] } as AsyncData<DataT | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>)> const asyncData = { ...nuxtApp._asyncData[key] } as { _default?: unknown } & AsyncData<DataT | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>)>
// Don't expose default function to end user
delete asyncData._default
asyncData.refresh = asyncData.execute = (opts = {}) => { asyncData.refresh = asyncData.execute = (opts = {}) => {
if (nuxtApp._asyncDataPromises[key]) { if (nuxtApp._asyncDataPromises[key]) {
@ -528,7 +532,7 @@ function clearNuxtDataByKey (nuxtApp: NuxtApp, key: string): void {
} }
if (nuxtApp._asyncData[key]) { if (nuxtApp._asyncData[key]) {
nuxtApp._asyncData[key]!.data.value = undefined nuxtApp._asyncData[key]!.data.value = resetAsyncDataToUndefined ? undefined : nuxtApp._asyncData[key]!._default()
nuxtApp._asyncData[key]!.error.value = asyncDataDefaults.errorValue nuxtApp._asyncData[key]!.error.value = asyncDataDefaults.errorValue
nuxtApp._asyncData[key]!.pending.value = false nuxtApp._asyncData[key]!.pending.value = false
nuxtApp._asyncData[key]!.status.value = 'idle' nuxtApp._asyncData[key]!.status.value = 'idle'

View File

@ -122,10 +122,12 @@ interface _NuxtApp {
_asyncDataPromises: Record<string, Promise<any> | undefined> _asyncDataPromises: Record<string, Promise<any> | undefined>
/** @internal */ /** @internal */
_asyncData: Record<string, { _asyncData: Record<string, {
data: Ref<any> data: Ref<unknown>
pending: Ref<boolean> pending: Ref<boolean>
error: Ref<Error | DefaultAsyncDataErrorValue> error: Ref<Error | DefaultAsyncDataErrorValue>
status: Ref<AsyncDataRequestStatus> status: Ref<AsyncDataRequestStatus>
/** @internal */
_default: () => unknown
} | undefined> } | undefined>
/** @internal */ /** @internal */

View File

@ -404,6 +404,7 @@ export const nuxtConfigTemplate: NuxtTemplate = {
value: ctx.nuxt.options.experimental.defaults.useAsyncData.value === 'null' ? null : undefined, value: ctx.nuxt.options.experimental.defaults.useAsyncData.value === 'null' ? null : undefined,
errorValue: ctx.nuxt.options.experimental.defaults.useAsyncData.errorValue === 'null' ? null : undefined, errorValue: ctx.nuxt.options.experimental.defaults.useAsyncData.errorValue === 'null' ? null : undefined,
})}`, })}`,
`export const resetAsyncDataToUndefined = ${ctx.nuxt.options.experimental.resetAsyncDataToUndefined}`,
`export const nuxtDefaultErrorValue = ${ctx.nuxt.options.future.compatibilityVersion === 4 ? 'undefined' : 'null'}`, `export const nuxtDefaultErrorValue = ${ctx.nuxt.options.future.compatibilityVersion === 4 ? 'undefined' : 'null'}`,
`export const fetchDefaults = ${JSON.stringify(fetchDefaults)}`, `export const fetchDefaults = ${JSON.stringify(fetchDefaults)}`,
`export const vueAppRootContainer = ${ctx.nuxt.options.app.rootId ? `'#${ctx.nuxt.options.app.rootId}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`, `export const vueAppRootContainer = ${ctx.nuxt.options.app.rootId ? `'#${ctx.nuxt.options.app.rootId}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`,

View File

@ -29,6 +29,7 @@ export default defineUntypedSchema({
* compileTemplate: true, * compileTemplate: true,
* templateUtils: true, * templateUtils: true,
* relativeWatchPaths: true, * relativeWatchPaths: true,
* resetAsyncDataToUndefined: true,
* defaults: { * defaults: {
* useAsyncData: { * useAsyncData: {
* deep: true * deep: true
@ -488,5 +489,15 @@ export default defineUntypedSchema({
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion !== 4) return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion !== 4)
}, },
}, },
/**
* Whether `clear` and `clearNuxtData` should reset async data to its _default_ value or update
* it to `null`/`undefined`.
*/
resetAsyncDataToUndefined: {
async $resolve (val, get) {
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion !== 4)
},
},
}, },
}) })