mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
feat(nuxt): add immediate
option for useAsyncData
and useFetch
(#5500)
Co-authored-by: Pooya Parsa <pooya@pi0.io>
This commit is contained in:
parent
577a7b681e
commit
fc2be9ed42
@ -23,6 +23,7 @@ type AsyncDataOptions<DataT> = {
|
|||||||
pick?: string[]
|
pick?: string[]
|
||||||
watch?: WatchSource[]
|
watch?: WatchSource[]
|
||||||
initialCache?: boolean
|
initialCache?: boolean
|
||||||
|
immediate?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RefreshOptions {
|
interface RefreshOptions {
|
||||||
@ -32,6 +33,7 @@ interface RefreshOptions {
|
|||||||
type AsyncData<DataT, ErrorT> = {
|
type AsyncData<DataT, ErrorT> = {
|
||||||
data: Ref<DataT | null>
|
data: Ref<DataT | null>
|
||||||
pending: Ref<boolean>
|
pending: Ref<boolean>
|
||||||
|
execute: () => Promise<void>
|
||||||
refresh: (opts?: RefreshOptions) => Promise<void>
|
refresh: (opts?: RefreshOptions) => Promise<void>
|
||||||
error: Ref<ErrorT | null>
|
error: Ref<ErrorT | null>
|
||||||
}
|
}
|
||||||
@ -51,6 +53,7 @@ type AsyncData<DataT, ErrorT> = {
|
|||||||
* _pick_: only pick specified keys in this array from the `handler` function result
|
* _pick_: only pick specified keys in this array from the `handler` function result
|
||||||
* _watch_: watch reactive sources to auto-refresh
|
* _watch_: watch reactive sources to auto-refresh
|
||||||
* _initialCache_: When set to `false`, will skip payload cache for initial fetch. (defaults to `true`)
|
* _initialCache_: When set to `false`, will skip payload cache for initial fetch. (defaults to `true`)
|
||||||
|
* _immediate_: When set to `false`, will prevent the request from firing immediately. (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.
|
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.
|
||||||
|
|
||||||
@ -58,7 +61,7 @@ Under the hood, `lazy: false` uses `<Suspense>` to block the loading of the rout
|
|||||||
|
|
||||||
* **data**: the result of the asynchronous function that is passed in
|
* **data**: the result of the asynchronous function that is passed in
|
||||||
* **pending**: a boolean indicating whether the data is still being fetched
|
* **pending**: a boolean indicating whether the data is still being fetched
|
||||||
* **refresh**: a function that can be used to refresh the data returned by the `handler` function
|
* **refresh**/**execute**: a function that can be used to refresh the data returned by the `handler` function
|
||||||
* **error**: an error object if the data fetching failed
|
* **error**: an error object if the data fetching failed
|
||||||
|
|
||||||
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
||||||
|
@ -19,6 +19,7 @@ type UseFetchOptions = {
|
|||||||
baseURL?: string
|
baseURL?: string
|
||||||
server?: boolean
|
server?: boolean
|
||||||
lazy?: boolean
|
lazy?: boolean
|
||||||
|
immediate?: boolean
|
||||||
default?: () => DataT
|
default?: () => DataT
|
||||||
transform?: (input: DataT) => DataT
|
transform?: (input: DataT) => DataT
|
||||||
pick?: string[]
|
pick?: string[]
|
||||||
@ -30,6 +31,7 @@ type AsyncData<DataT> = {
|
|||||||
data: Ref<DataT>
|
data: Ref<DataT>
|
||||||
pending: Ref<boolean>
|
pending: Ref<boolean>
|
||||||
refresh: () => Promise<void>
|
refresh: () => Promise<void>
|
||||||
|
execute: () => Promise<void>
|
||||||
error: Ref<Error | boolean>
|
error: Ref<Error | boolean>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -51,6 +53,7 @@ type AsyncData<DataT> = {
|
|||||||
* `watch`: watch reactive sources to auto-refresh.
|
* `watch`: watch reactive sources to auto-refresh.
|
||||||
* `initialCache`: When set to `false`, will skip payload cache for initial fetch (defaults to `true`).
|
* `initialCache`: When set to `false`, will skip payload cache for initial fetch (defaults to `true`).
|
||||||
* `transform`: A function that can be used to alter `handler` function result after resolving.
|
* `transform`: A function that can be used to alter `handler` function result after resolving.
|
||||||
|
* `immediate`: When set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||||
|
|
||||||
::alert{type=warning}
|
::alert{type=warning}
|
||||||
If you provide a function or ref as the `url` parameter, or if you provide functions as arguments to the `options` parameter, then the `useFetch` call will not match other `useFetch` calls elsewhere in your codebase, even if the options seem to be identical. If you wish to force a match, you may provide your own key in `options`.
|
If you provide a function or ref as the `url` parameter, or if you provide functions as arguments to the `options` parameter, then the `useFetch` call will not match other `useFetch` calls elsewhere in your codebase, even if the options seem to be identical. If you wish to force a match, you may provide your own key in `options`.
|
||||||
@ -60,7 +63,7 @@ If you provide a function or ref as the `url` parameter, or if you provide funct
|
|||||||
|
|
||||||
* **data**: the result of the asynchronous function that is passed in.
|
* **data**: the result of the asynchronous function that is passed in.
|
||||||
* **pending**: a boolean indicating whether the data is still being fetched.
|
* **pending**: a boolean indicating whether the data is still being fetched.
|
||||||
* **refresh**: a function that can be used to refresh the data returned by the `handler` function.
|
* **refresh**/**execute** : a function that can be used to refresh the data returned by the `handler` function.
|
||||||
* **error**: an error object if the data fetching failed.
|
* **error**: an error object if the data fetching failed.
|
||||||
|
|
||||||
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
||||||
|
@ -29,16 +29,18 @@ export interface AsyncDataOptions<
|
|||||||
pick?: PickKeys
|
pick?: PickKeys
|
||||||
watch?: MultiWatchSources
|
watch?: MultiWatchSources
|
||||||
initialCache?: boolean
|
initialCache?: boolean
|
||||||
|
immediate?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RefreshOptions {
|
export interface AsyncDataExecuteOptions {
|
||||||
_initial?: boolean
|
_initial?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface _AsyncData<DataT, ErrorT> {
|
export interface _AsyncData<DataT, ErrorT> {
|
||||||
data: Ref<DataT | null>
|
data: Ref<DataT | null>
|
||||||
pending: Ref<boolean>
|
pending: Ref<boolean>
|
||||||
refresh: (opts?: RefreshOptions) => Promise<void>
|
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||||
|
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||||
error: Ref<ErrorT | null>
|
error: Ref<ErrorT | null>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +96,7 @@ export function useAsyncData<
|
|||||||
}
|
}
|
||||||
options.lazy = options.lazy ?? (options as any).defer ?? false
|
options.lazy = options.lazy ?? (options as any).defer ?? false
|
||||||
options.initialCache = options.initialCache ?? true
|
options.initialCache = options.initialCache ?? true
|
||||||
|
options.immediate = options.immediate ?? true
|
||||||
|
|
||||||
// Setup nuxt instance payload
|
// Setup nuxt instance payload
|
||||||
const nuxt = useNuxtApp()
|
const nuxt = useNuxtApp()
|
||||||
@ -111,7 +114,7 @@ export function useAsyncData<
|
|||||||
// TODO: Else, Soemhow check for confliciting keys with different defaults or fetcher
|
// TODO: Else, Soemhow check for confliciting keys with different defaults or fetcher
|
||||||
const asyncData = { ...nuxt._asyncData[key] } as AsyncData<DataT, DataE>
|
const asyncData = { ...nuxt._asyncData[key] } as AsyncData<DataT, DataE>
|
||||||
|
|
||||||
asyncData.refresh = (opts = {}) => {
|
asyncData.refresh = asyncData.execute = (opts = {}) => {
|
||||||
// Avoid fetching same key more than once at a time
|
// Avoid fetching same key more than once at a time
|
||||||
if (nuxt._asyncDataPromises[key]) {
|
if (nuxt._asyncDataPromises[key]) {
|
||||||
return nuxt._asyncDataPromises[key]
|
return nuxt._asyncDataPromises[key]
|
||||||
@ -160,7 +163,7 @@ export function useAsyncData<
|
|||||||
const fetchOnServer = options.server !== false && nuxt.payload.serverRendered
|
const fetchOnServer = options.server !== false && nuxt.payload.serverRendered
|
||||||
|
|
||||||
// Server side
|
// Server side
|
||||||
if (process.server && fetchOnServer) {
|
if (process.server && fetchOnServer && options.immediate) {
|
||||||
const promise = initialFetch()
|
const promise = initialFetch()
|
||||||
onServerPrefetch(() => promise)
|
onServerPrefetch(() => promise)
|
||||||
}
|
}
|
||||||
@ -184,11 +187,11 @@ export function useAsyncData<
|
|||||||
if (fetchOnServer && nuxt.isHydrating && key in nuxt.payload.data) {
|
if (fetchOnServer && nuxt.isHydrating && key in nuxt.payload.data) {
|
||||||
// 1. Hydration (server: true): no fetch
|
// 1. Hydration (server: true): no fetch
|
||||||
asyncData.pending.value = false
|
asyncData.pending.value = false
|
||||||
} else if (instance && ((nuxt.payload.serverRendered && nuxt.isHydrating) || options.lazy)) {
|
} else if (instance && ((nuxt.payload.serverRendered && nuxt.isHydrating) || options.lazy) && options.immediate) {
|
||||||
// 2. Initial load (server: false): fetch on mounted
|
// 2. Initial load (server: false): fetch on mounted
|
||||||
// 3. Initial load or navigation (lazy: true): fetch on mounted
|
// 3. Initial load or navigation (lazy: true): fetch on mounted
|
||||||
instance._nuxtOnBeforeMountCbs.push(initialFetch)
|
instance._nuxtOnBeforeMountCbs.push(initialFetch)
|
||||||
} else {
|
} else if (options.immediate) {
|
||||||
// 4. Navigation (lazy: false) - or plugin usage: await fetch
|
// 4. Navigation (lazy: false) - or plugin usage: await fetch
|
||||||
initialFetch()
|
initialFetch()
|
||||||
}
|
}
|
||||||
|
36
test/fixtures/basic/pages/useAsyncData/immediate.vue
vendored
Normal file
36
test/fixtures/basic/pages/useAsyncData/immediate.vue
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
Single
|
||||||
|
<div>
|
||||||
|
data: {{ data }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const called = ref(0)
|
||||||
|
const { data, execute } = await useAsyncData(() => Promise.resolve(++called.value), { immediate: false })
|
||||||
|
|
||||||
|
if (called.value !== 0) {
|
||||||
|
throw new Error('Handled should have not been called')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.server && data.value !== null) {
|
||||||
|
throw new Error('Initial data should be null: ' + data.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
await execute()
|
||||||
|
await execute()
|
||||||
|
|
||||||
|
if (process.server && called.value as number !== 2) {
|
||||||
|
throw new Error('Should have been called once after execute (server) but called ' + called.value + ' times')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.client && called.value as number !== 2) {
|
||||||
|
throw new Error('Should have been called once after execute (client) but called ' + called.value + ' times')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.value !== 2) {
|
||||||
|
throw new Error('Data should be 1 after execute')
|
||||||
|
}
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user