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 dedupe option for data fetching composables #24564

Merged
merged 21 commits into from Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/3.api/2.composables/use-async-data.md
Expand Up @@ -64,6 +64,9 @@ const { data: posts } = await useAsyncData(
- `pick`: only pick specified keys in this array from the `handler` function result
- `watch`: watch reactive sources to auto-refresh
- `deep`: return data in a deep ref object (it is `true` by default). It can be set to `false` to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive.
- `dedupe`: avoid fetching same key more than once at a time (defaults to `cancel`). Possible options:
- `cancel` (or true) - cancels existing requests when a new one is made.
- `defer` (or false) - does not make new requests at all if there is a pending request.

::callout
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 Expand Up @@ -100,11 +103,14 @@ function useAsyncData<DataT, DataE>(
options?: AsyncDataOptions<DataT>
): Promise<AsyncData<DataT, DataE>

type Dedupe = boolean | 'cancel' | 'defer'

type AsyncDataOptions<DataT> = {
server?: boolean
lazy?: boolean
immediate?: boolean
deep?: boolean
dedupe?: Dedupe
default?: () => DataT | Ref<DataT> | null
transform?: (input: DataT) => DataT
pick?: string[]
Expand All @@ -122,7 +128,7 @@ type AsyncData<DataT, ErrorT> = {
};

interface AsyncDataExecuteOptions {
dedupe?: boolean
dedupe?: Dedupe
}

type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
Expand Down
12 changes: 10 additions & 2 deletions packages/nuxt/src/app/composables/asyncData.ts
Expand Up @@ -34,6 +34,8 @@ export type KeyOfRes<Transform extends _Transform> = KeysOf<ReturnType<Transform

export type MultiWatchSources = (WatchSource<unknown> | object)[]

export type Dedupe = boolean | 'cancel' | 'defer'

export interface AsyncDataOptions<
ResT,
DataT = ResT,
Expand All @@ -49,6 +51,7 @@ export interface AsyncDataOptions<
watch?: MultiWatchSources
immediate?: boolean
deep?: boolean
dedupe?: Dedupe
}

export interface AsyncDataExecuteOptions {
Expand All @@ -58,7 +61,7 @@ export interface AsyncDataExecuteOptions {
* not be cancelled, but their result will not affect the data/pending state - and any
* previously awaited promises will not resolve until this new request resolves.
*/
dedupe?: boolean
dedupe?: Dedupe
}

export interface _AsyncData<DataT, ErrorT> {
Expand All @@ -72,6 +75,10 @@ export interface _AsyncData<DataT, ErrorT> {

export type AsyncData<Data, Error> = _AsyncData<Data, Error> & Promise<_AsyncData<Data, Error>>

const deferDedupeSet = new Set(['defer', false])

const isDefer = (dedupe?: Dedupe) => deferDedupeSet.has(dedupe || 'cancel')

export function useAsyncData<
ResT,
DataE = Error,
Expand Down Expand Up @@ -150,6 +157,7 @@ export function useAsyncData<
options.lazy = options.lazy ?? false
options.immediate = options.immediate ?? true
options.deep = options.deep ?? asyncDataDefaults.deep
options.dedupe = options.dedupe ?? 'cancel'

const hasCachedData = () => ![null, undefined].includes(options.getCachedData!(key) as any)

Expand All @@ -172,7 +180,7 @@ export function useAsyncData<

asyncData.refresh = asyncData.execute = (opts = {}) => {
if (nuxt._asyncDataPromises[key]) {
if (opts.dedupe === false) {
if (isDefer(opts.dedupe ?? options.dedupe)) {
// Avoid fetching same key more than once at a time
return nuxt._asyncDataPromises[key]!
}
Expand Down