diff --git a/.agents/skills/data-client-manager/references/Controller.vue.md b/.agents/skills/data-client-manager/references/Controller.vue.md index ed2b210dc94a..eb4ab5849bce 100644 --- a/.agents/skills/data-client-manager/references/Controller.vue.md +++ b/.agents/skills/data-client-manager/references/Controller.vue.md @@ -239,6 +239,38 @@ until the refetch resolves. ``` +> **Tip: Show the Suspense fallback again** +> +> Remount the component with a new `key` after invalidating; the new instance suspends on the invalid data. +> `timeout="0"` makes `` show its fallback while it waits instead of the old content. Remounting +> resets the component's local state. +> +> ```html title="ArticlePage.vue" +> +> +> +> ``` + > **Tip: Invalidate many endpoints at once** > > Use [schema.Invalidate](https://dataclient.io/rest/api/Invalidate) to invalidate every endpoint that contains a given entity. @@ -580,7 +612,7 @@ export enum ExpiryStatus { ##### Invalid -- Will always suspend +- Will always suspend during setup; mounted components [keep their current data](https://dataclient.io/vue/concepts/expiry-policy#invalidate) until the refetch resolves - Will always fetch #### expiresAt diff --git a/.agents/skills/data-client-vue/references/Controller.md b/.agents/skills/data-client-vue/references/Controller.md index 085a6a3ae926..5028bf0c4f40 100644 --- a/.agents/skills/data-client-vue/references/Controller.md +++ b/.agents/skills/data-client-vue/references/Controller.md @@ -239,6 +239,38 @@ until the refetch resolves. ``` +> **Tip: Show the Suspense fallback again** +> +> Remount the component with a new `key` after invalidating; the new instance suspends on the invalid data. +> `timeout="0"` makes `` show its fallback while it waits instead of the old content. Remounting +> resets the component's local state. +> +> ```html title="ArticlePage.vue" +> +> +> +> ``` + > **Tip: Invalidate many endpoints at once** > > Use [schema.Invalidate](https://dataclient.io/rest/api/Invalidate) to invalidate every endpoint that contains a given entity. @@ -580,7 +612,7 @@ export enum ExpiryStatus { ##### Invalid -- Will always suspend +- Will always suspend during setup; mounted components [keep their current data](https://dataclient.io/vue/concepts/expiry-policy#invalidate) until the refetch resolves - Will always fetch #### expiresAt diff --git a/.agents/skills/data-client-vue/references/useSuspense.md b/.agents/skills/data-client-vue/references/useSuspense.md index 89076c4b2b68..9dba0dda3b59 100644 --- a/.agents/skills/data-client-vue/references/useSuspense.md +++ b/.agents/skills/data-client-vue/references/useSuspense.md @@ -94,17 +94,19 @@ export const getProfile = new Endpoint( Cache policy is [Stale-While-Revalidate](https://tools.ietf.org/html/rfc5861) by default but also [configurable](https://dataclient.io/vue/concepts/expiry-policy). -| Expiry Status | Fetch | Suspend | Error | Conditions | -| ------------- | --------------- | ------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Invalid | yes1 | yes | no | not in store, [deletion](https://dataclient.io/rest/api/resource#delete), [invalidation](./Controller.md#invalidate), [invalidIfStale](https://dataclient.io/vue/concepts/expiry-policy#endpointinvalidifstale) | -| Stale | yes1 | no | no | (first-render, arg change) & [expiry < now](https://dataclient.io/vue/concepts/expiry-policy) | -| Valid | no | no | maybe2 | fetch completion | -| | no | no | no | `null` used as second argument | +| Expiry Status | Fetch | Suspend | Error | Conditions | +| ------------- | --------------- | --------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Invalid | yes1 | yes3 | no | not in store, [deletion](https://dataclient.io/rest/api/resource#delete), [invalidation](./Controller.md#invalidate), [invalidIfStale](https://dataclient.io/vue/concepts/expiry-policy#endpointinvalidifstale) | +| Stale | yes1 | no | no | (first-render, arg change) & [expiry < now](https://dataclient.io/vue/concepts/expiry-policy) | +| Valid | no | no | maybe2 | fetch completion | +| | no | no | no | `null` used as second argument | > **Note** > > 1. Identical fetches are automatically deduplicated > 2. [Hard errors](https://dataclient.io/vue/concepts/error-policy#hard) to be [caught](./data-dependency.md#async-fallbacks) by [onErrorCaptured()](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured) +> +> 3) Only during setup; mounted components [keep their current data](https://dataclient.io/vue/concepts/expiry-policy#invalidate) until the refetch resolves. > **Tip: Conditional Dependencies** > diff --git a/docs/core/api/Controller.md b/docs/core/api/Controller.md index f123340a4938..2dd593262238 100644 --- a/docs/core/api/Controller.md +++ b/docs/core/api/Controller.md @@ -431,6 +431,44 @@ function ArticleName({ id }: { id: string }) { ::: +::::vue + +:::tip[Show the Suspense fallback again] + +Remount the component with a new `key` after invalidating; the new instance suspends on the invalid data. +`timeout="0"` makes `` show its fallback while it waits instead of the old content. Remounting +resets the component's local state. + +```html title="ArticlePage.vue" + + + +``` + +::: + +:::: + ::::react :::tip @@ -949,7 +987,7 @@ export enum ExpiryStatus { ##### Invalid -- Will always suspend +- Will always suspend:vue[ during setup; mounted components [keep their current data](../concepts/expiry-policy.md#invalidate) until the refetch resolves] - Will always fetch #### expiresAt diff --git a/docs/core/api/useSuspense.md b/docs/core/api/useSuspense.md index 7cc9881e04d0..dd9886ca41fd 100644 --- a/docs/core/api/useSuspense.md +++ b/docs/core/api/useSuspense.md @@ -204,20 +204,26 @@ render(); Cache policy is [Stale-While-Revalidate](https://tools.ietf.org/html/rfc5861) by default but also [configurable](../concepts/expiry-policy.md). -| Expiry Status | Fetch | Suspend | Error | Conditions | -| ------------- | --------------- | ------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Invalid | yes1 | yes | no | not in store, [deletion](/rest/api/resource#delete), [invalidation](./Controller.md#invalidate), [invalidIfStale](../concepts/expiry-policy.md#endpointinvalidifstale) | -| Stale | yes1 | no | no | (first-render, arg change) & [expiry < now](../concepts/expiry-policy.md) | -| Valid | no | no | maybe2 | fetch completion | -| | no | no | no | `null` used as second argument | +| Expiry Status | Fetch | Suspend | Error | Conditions | +| ------------- | --------------- | --------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Invalid | yes1 | yes:vue[3] | no | not in store, [deletion](/rest/api/resource#delete), [invalidation](./Controller.md#invalidate), [invalidIfStale](../concepts/expiry-policy.md#endpointinvalidifstale) | +| Stale | yes1 | no | no | (first-render, arg change) & [expiry < now](../concepts/expiry-policy.md) | +| Valid | no | no | maybe2 | fetch completion | +| | no | no | no | `null` used as second argument | -:::note +::::note 1. Identical fetches are automatically deduplicated 2. [Hard errors](../concepts/error-policy.md#hard) to be [caught](../getting-started/data-dependency#async-fallbacks) by :react[[Error Boundaries](./AsyncBoundary.md)]:vue[[onErrorCaptured()](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured)] +:::vue + +3. Only during setup; mounted components [keep their current data](../concepts/expiry-policy.md#invalidate) until the refetch resolves. + ::: +:::: + ::::react :::info[React Native] diff --git a/packages/core/src/controller/Controller.ts b/packages/core/src/controller/Controller.ts index 6d590b70b04b..5afa12a14a33 100644 --- a/packages/core/src/controller/Controller.ts +++ b/packages/core/src/controller/Controller.ts @@ -187,7 +187,8 @@ export default class Controller< }; /** - * Forces refetching and suspense on useSuspense with the same Endpoint and parameters. + * Forces refetching on useSuspense with the same Endpoint and parameters, suspending until it resolves + * (mounted Vue components keep their current data instead). * @see https://dataclient.io/docs/api/Controller#invalidate */ invalidate = ( @@ -203,7 +204,8 @@ export default class Controller< : Promise.resolve(); /** - * Forces refetching and suspense on useSuspense on all matching endpoint result keys. + * Forces refetching on useSuspense on all matching endpoint result keys, suspending until it resolves + * (mounted Vue components keep their current data instead). * @see https://dataclient.io/docs/api/Controller#invalidateAll * @returns Promise that resolves when invalidation is commited. */ diff --git a/website/src/components/Playground/editor-types/@data-client/core.d.ts b/website/src/components/Playground/editor-types/@data-client/core.d.ts index a21c6d45e2e1..7452fe3bb6a3 100644 --- a/website/src/components/Playground/editor-types/@data-client/core.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/core.d.ts @@ -820,12 +820,14 @@ declare class Controller { update?: EndpointUpdateFunction; }>(endpoint: E, ...args: readonly [...Parameters]) => E['schema'] extends undefined | null ? ReturnType | ResolveType : Promise> | Denormalize; /** - * Forces refetching and suspense on useSuspense with the same Endpoint and parameters. + * Forces refetching on useSuspense with the same Endpoint and parameters, suspending until it resolves + * (mounted Vue components keep their current data instead). * @see https://dataclient.io/docs/api/Controller#invalidate */ invalidate: (endpoint: E, ...args: readonly [...Parameters] | readonly [null]) => Promise; /** - * Forces refetching and suspense on useSuspense on all matching endpoint result keys. + * Forces refetching on useSuspense on all matching endpoint result keys, suspending until it resolves + * (mounted Vue components keep their current data instead). * @see https://dataclient.io/docs/api/Controller#invalidateAll * @returns Promise that resolves when invalidation is commited. */ diff --git a/website/src/components/Playground/editor-types/@data-client/core/mock.d.ts b/website/src/components/Playground/editor-types/@data-client/core/mock.d.ts index 3cfcce0981f2..8309965b151c 100644 --- a/website/src/components/Playground/editor-types/@data-client/core/mock.d.ts +++ b/website/src/components/Playground/editor-types/@data-client/core/mock.d.ts @@ -710,12 +710,14 @@ declare class Controller { update?: EndpointUpdateFunction; }>(endpoint: E, ...args: readonly [...Parameters]) => E['schema'] extends undefined | null ? ReturnType | ResolveType$1 : Promise> | Denormalize$1; /** - * Forces refetching and suspense on useSuspense with the same Endpoint and parameters. + * Forces refetching on useSuspense with the same Endpoint and parameters, suspending until it resolves + * (mounted Vue components keep their current data instead). * @see https://dataclient.io/docs/api/Controller#invalidate */ invalidate: (endpoint: E, ...args: readonly [...Parameters] | readonly [null]) => Promise; /** - * Forces refetching and suspense on useSuspense on all matching endpoint result keys. + * Forces refetching on useSuspense on all matching endpoint result keys, suspending until it resolves + * (mounted Vue components keep their current data instead). * @see https://dataclient.io/docs/api/Controller#invalidateAll * @returns Promise that resolves when invalidation is commited. */