From 811bfc18a17ccdd82bcf5e42760640690a2dce8b Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 22 May 2024 15:42:19 +0100 Subject: [PATCH] feat(nuxt): respect defaults when clearing asyncData (#27295) --- docs/1.getting-started/12.upgrade.md | 27 +++++++++++++++++++ .../nuxt/src/app/composables/asyncData.ts | 10 ++++--- packages/nuxt/src/app/nuxt.ts | 4 ++- packages/nuxt/src/core/templates.ts | 1 + packages/schema/src/config/experimental.ts | 11 ++++++++ 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/docs/1.getting-started/12.upgrade.md b/docs/1.getting-started/12.upgrade.md index 4e0301db96..c8e6c66ef0 100644 --- a/docs/1.getting-started/12.upgrade.md +++ b/docs/1.getting-started/12.upgrade.md @@ -48,6 +48,7 @@ export default defineNuxtConfig({ // experimental: { // sharedPrerenderData: false, // compileTemplate: true, + // resetAsyncDataToUndefined: true, // templateUtils: true, // relativeWatchPaths: true, // 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. +#### 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` 🚦 **Impact Level**: Minimal diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 262a1ee54b..92e62b32f7 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -8,7 +8,7 @@ import { createError } from './error' import { onNuxtReady } from './ready' // @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 import type { DefaultAsyncDataErrorValue, DefaultAsyncDataValue } from '#app/defaults' @@ -267,11 +267,15 @@ export function useAsyncData< pending: ref(!hasCachedData()), error: toRef(nuxtApp.payload._errors, key), status: ref('idle'), + _default: options.default!, } } // TODO: Else, somehow check for conflicting keys with different defaults or fetcher - const asyncData = { ...nuxtApp._asyncData[key] } as AsyncData)> + const asyncData = { ...nuxtApp._asyncData[key] } as { _default?: unknown } & AsyncData)> + + // Don't expose default function to end user + delete asyncData._default asyncData.refresh = asyncData.execute = (opts = {}) => { if (nuxtApp._asyncDataPromises[key]) { @@ -528,7 +532,7 @@ function clearNuxtDataByKey (nuxtApp: NuxtApp, key: string): void { } 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]!.pending.value = false nuxtApp._asyncData[key]!.status.value = 'idle' diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index ae3137a7e2..8222527514 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -122,10 +122,12 @@ interface _NuxtApp { _asyncDataPromises: Record | undefined> /** @internal */ _asyncData: Record + data: Ref pending: Ref error: Ref status: Ref + /** @internal */ + _default: () => unknown } | undefined> /** @internal */ diff --git a/packages/nuxt/src/core/templates.ts b/packages/nuxt/src/core/templates.ts index c7246f42ee..520f3986e7 100644 --- a/packages/nuxt/src/core/templates.ts +++ b/packages/nuxt/src/core/templates.ts @@ -404,6 +404,7 @@ export const nuxtConfigTemplate: NuxtTemplate = { value: ctx.nuxt.options.experimental.defaults.useAsyncData.value === '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 fetchDefaults = ${JSON.stringify(fetchDefaults)}`, `export const vueAppRootContainer = ${ctx.nuxt.options.app.rootId ? `'#${ctx.nuxt.options.app.rootId}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`, diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index 2d8b14d7e3..8d01cf72a4 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -29,6 +29,7 @@ export default defineUntypedSchema({ * compileTemplate: true, * templateUtils: true, * relativeWatchPaths: true, + * resetAsyncDataToUndefined: true, * defaults: { * useAsyncData: { * deep: true @@ -488,5 +489,15 @@ export default defineUntypedSchema({ return val ?? ((await get('future') as Record).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).compatibilityVersion !== 4) + }, + }, }, })