Skip to content
This repository was archived by the owner on Apr 6, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/content/3.docs/1.usage/1.data-fetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ Within your pages, components and plugins you can use `useAsyncData` to get acce
const {
data: Ref<DataT>,
pending: Ref<boolean>,
refresh: (force?: boolean) => Promise<void>,
refresh: () => Promise<void>,
error?: any
} = useAsyncData(
key: string,
handler: (ctx?: NuxtApp) => Promise<Object>,
options?: {
options?: {
lazy: boolean,
server: boolean,
watch: WatchSource[]
Expand All @@ -40,6 +40,7 @@ const {
* _transform_: a function that can be used to alter `handler` function result after resolving
* _pick_: only pick specified keys in this array from `handler` function result
* _watch_: watch reactive sources to auto refresh
* _initialCache_: When set to `false`, will skip payload cache for initial fetch. (defaults to `true`)

Under the hood, `lazy: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `lazy: true` and implementing a loading state instead for a snappier user experience.

Expand Down
24 changes: 18 additions & 6 deletions packages/nuxt3/src/app/composables/asyncData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ export interface AsyncDataOptions<
transform?: Transform
pick?: PickKeys
watch?: MultiWatchSources
initialCache?: boolean
}

export interface RefreshOptions {
_initial?: boolean
}

export interface _AsyncData<DataT> {
data: Ref<DataT>
pending: Ref<boolean>
refresh: (force?: boolean) => Promise<void>
refresh: (opts?: RefreshOptions) => Promise<void>
error?: any
}

Expand Down Expand Up @@ -58,6 +63,7 @@ export function useAsyncData<
console.warn('[useAsyncData] `defer` has been renamed to `lazy`. Support for `defer` will be removed in RC.')
}
options.lazy = options.lazy ?? (options as any).defer ?? false
options.initialCache = options.initialCache ?? true

// Setup nuxt instance payload
const nuxt = useNuxtApp()
Expand All @@ -81,11 +87,15 @@ export function useAsyncData<
error: ref(nuxt.payload._errors[key] ?? null)
} as AsyncData<DataT>

asyncData.refresh = (force?: boolean) => {
asyncData.refresh = (opts = {}) => {
// Avoid fetching same key more than once at a time
if (nuxt._asyncDataPromises[key] && !force) {
if (nuxt._asyncDataPromises[key]) {
return nuxt._asyncDataPromises[key]
}
// Avoid fetching same key that is already fetched
if (opts._initial && options.initialCache && nuxt.payload.data[key] !== undefined) {
return nuxt.payload.data[key]
}
asyncData.pending.value = true
// TODO: Cancel previous promise
// TODO: Handle immediate errors
Expand Down Expand Up @@ -115,11 +125,13 @@ export function useAsyncData<
return nuxt._asyncDataPromises[key]
}

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

const fetchOnServer = options.server !== false && nuxt.payload.serverRendered

// Server side
if (process.server && fetchOnServer) {
const promise = asyncData.refresh()
const promise = initialFetch()
onServerPrefetch(() => promise)
}

Expand All @@ -131,10 +143,10 @@ export function useAsyncData<
} else if (instance && (nuxt.isHydrating || options.lazy)) {
// 2. Initial load (server: false): fetch on mounted
// 3. Navigation (lazy: true): fetch on mounted
instance._nuxtOnBeforeMountCbs.push(asyncData.refresh)
instance._nuxtOnBeforeMountCbs.push(initialFetch)
} else {
// 4. Navigation (lazy: false) - or plugin usage: await fetch
asyncData.refresh()
initialFetch()
}
if (options.watch) {
watch(options.watch, () => asyncData.refresh())
Expand Down
24 changes: 18 additions & 6 deletions packages/nuxt3/src/app/composables/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ import { useAsyncData } from './asyncData'

export type FetchResult<ReqT extends FetchRequest> = TypedInternalResponse<ReqT, unknown>

export type UseFetchOptions<
export interface UseFetchOptions<
DataT,
Transform extends _Transform<DataT, any> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> = AsyncDataOptions<DataT, Transform, PickKeys> & FetchOptions & { key?: string }
> extends
AsyncDataOptions<DataT, Transform, PickKeys>,
FetchOptions
{
key?: string
}

export function useFetch<
ResT = void,
Expand All @@ -32,15 +37,22 @@ export function useFetch<
return isRef(r) ? r.value : r
})

const asyncData = useAsyncData(key, () => {
return $fetch(_request.value, opts) as Promise<_ResT>
}, {
const _fetchOptions = {
...opts,
cache: typeof opts.cache === 'boolean' ? undefined : opts.cache
}

const _asyncDataOptions: AsyncDataOptions<any> = {
...opts,
watch: [
_request,
...(opts.watch || [])
]
})
}

const asyncData = useAsyncData(key, () => {
return $fetch(_request.value, _fetchOptions) as Promise<_ResT>
}, _asyncDataOptions)

return asyncData
}
Expand Down