Repository: nuxt/nuxt
File: packages/nuxt/src/app/composables/asyncData.ts (TypeScript)

Code before:
  if (typeof args[0] !== 'string') { args.unshift(autoKey) }

  // eslint-disable-next-line prefer-const
  let [key, handler, options = {}] = args as [string, (ctx?: NuxtApp) => Promise<ResT>, AsyncDataOptions<ResT, DataT, PickKeys>]

  // Validate arguments
  if (typeof key !== 'string') {
    throw new TypeError('[nuxt] [asyncData] key must be a string.')
  }
  if (typeof handler !== 'function') {
    throw new TypeError('[nuxt] [asyncData] handler must be a function.')
  }

  // Apply defaults
  options.server = options.server ?? true
  options.default = options.default ?? getDefault

  options.lazy = options.lazy ?? false
  options.immediate = options.immediate ?? true

  options.forcePayloadExtraction = options.forcePayloadExtraction ?? false

  // Setup nuxt instance payload
  const nuxt = useNuxtApp()

  const getCachedData = () => (nuxt.isHydrating || options.forcePayloadExtraction) ? nuxt.payload.data[key] : nuxt.static.data[key]
  const hasCachedData = () => getCachedData() !== undefined

  // Create or use a shared asyncData entity
  if (!nuxt._asyncData[key]) {
    nuxt._asyncData[key] = {
      data: ref(getCachedData() ?? options.default?.() ?? null),
      pending: ref(!hasCachedData()),
      error: toRef(nuxt.payload._errors, key)
    }
  }
  // TODO: Else, somehow check for conflicting keys with different defaults or fetcher
  const asyncData = { ...nuxt._asyncData[key] } as AsyncData<DataT, DataE>

  asyncData.refresh = asyncData.execute = (opts = {}) => {
    if (nuxt._asyncDataPromises[key]) {
      if (opts.dedupe === false) {
        // Avoid fetching same key more than once at a time
        return nuxt._asyncDataPromises[key]
      }
      (nuxt._asyncDataPromises[key] as any).cancelled = true
    }
    // Avoid fetching same key that is already fetched
    if (opts._initial && hasCachedData()) {
      return getCachedData()
    }

Review comment (bug):
I'm reluctant to merge this PR as it's really a feature to work around a bug.

What about instead supporting a `cache`/`store` option that defaults to `nuxt.isHydrating ? nuxt.payload.data : nuxt.static.data`? Then this could be set to a custom location, or forced to `nuxt.payload.data`, which would have the same effect as forcing payload extraction.

It could be part of https://github.com/nuxt/nuxt/issues/15445.

Thoughts @pi0?

Code after:
  ResT,
  DataE = Error,
  DataT = ResT,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
  DefaultT = null,
> (
  key: string,
  handler: (ctx?: NuxtApp) => Promise<ResT>,
  options?: AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>
): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, DataE | null>
export function useAsyncData<
  ResT,
  DataE = Error,
  DataT = ResT,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
  DefaultT = DataT,
> (
  key: string,
  handler: (ctx?: NuxtApp) => Promise<ResT>,
  options?: AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>
): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, DataE | null>
export function useAsyncData<
  ResT,
  DataE = Error,
  DataT = ResT,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
  DefaultT = null,
> (...args: any[]): AsyncData<PickFrom<DataT, PickKeys>, DataE | null> {
  const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined
  if (typeof args[0] !== 'string') { args.unshift(autoKey) }

  // eslint-disable-next-line prefer-const
  let [key, handler, options = {}] = args as [string, (ctx?: NuxtApp) => Promise<ResT>, AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>]

  // Validate arguments
  if (typeof key !== 'string') {
    throw new TypeError('[nuxt] [asyncData] key must be a string.')
  }
  if (typeof handler !== 'function') {
    throw new TypeError('[nuxt] [asyncData] handler must be a function.')
  }

  // Setup nuxt instance payload
  const nuxt = useNuxtApp()

  // Used to get default values
  const getDefault = () => null
  const getDefaultCachedData = () => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]

  // Apply defaults
  options.server = options.server ?? true