useDLE() - [D]ata [L]oading [E]rror
High performance async data rendering without overfetching. With fetch meta data.
In case you cannot use suspense, useDLE() is just like useSuspense() but returns [D]ata [L]oading [E]rror values.
useDLE() is reactive to data mutations; rerendering only when necessary.
Usage
ProfileResource
ProfileList.vue
<script setup lang="ts"> import { useDLE } from '@data-client/vue'; import { ProfileResource } from './ProfileResource'; const { data, loading, error } = useDLE(ProfileResource.getList); </script> <template> <div v-if="error">Error {{ error.status }}</div> <Loading v-else-if="loading || !data" /> <div v-else> <div class="listItem" v-for="profile in data" :key="profile.pk()"> <Avatar :src="profile.avatar" /> <div> <h4>{{ profile.fullName }}</h4> <p>{{ profile.bio }}</p> </div> </div> </div> </template>
Behavior
data, loading and error are each a ComputedRef.
Destructure them at the top level of <script setup> so they are unwrapped in the template. The table
below describes their .value.
| Expiry Status | Fetch | Data | Loading | Error | Conditions |
|---|---|---|---|---|---|
| Invalid | yes1 | undefined | true | false | not in store, deletion, invalidation, invalidIfStale |
| Stale | yes1 | denormalized | false | false | (first-render, arg change) & expiry < now |
| Valid | no | denormalized | false | maybe2 | fetch completion |
| no | undefined | false | false | null used as second argument |
note
- Identical fetches are automatically deduplicated
- Hard errors to be caught by onErrorCaptured()
:::
Conditional Dependencies
Use null as the second argument to any Data Client hook means "do nothing."
// todo could be undefined if id is undefined
const todo = useDLE(
TodoResource.get,
computed(() => (id.value ? { id: id.value } : null)),
);
Types
function useDLE(
endpoint: ReadEndpoint,
...args: MaybeRefsOrGetters<Parameters<typeof endpoint>> | [null]
): {
data: ComputedRef<DenormalizeNullable<typeof endpoint.schema>>;
loading: ComputedRef<boolean>;
error: ComputedRef<ErrorTypes | undefined>;
};
Arguments can be plain values or refs (including computed); the results update when they change.
Examples
Detail
ProfileResource
ProfileDetail.vue
<script setup lang="ts"> import { useDLE } from '@data-client/vue'; import { ProfileResource } from './ProfileResource'; const { data: profile, loading, error, } = useDLE(ProfileResource.get, { id: 1 }); </script> <template> <div v-if="error">Error {{ error.status }}</div> <Loading v-else-if="loading || !profile" /> <div v-else class="listItem"> <Avatar :src="profile.avatar" /> <div> <h4>{{ profile.fullName }}</h4> <p>{{ profile.bio }}</p> </div> </div> </template>
Conditional
null will avoid binding and fetching data
▶Resources
▶PostWithAuthor.vue
<script setup lang="ts"> import { computed } from 'vue'; import { useDLE } from '@data-client/vue'; import { PostResource, UserResource } from './Resources'; const props = defineProps<{ id: string }>(); const { data: post, loading: postLoading, error: postError, } = useDLE(PostResource.get, { id: props.id }); const { data: author, loading: authorLoading, error: authorError, } = useDLE( UserResource.get, computed(() => post.value?.userId ? { id: post.value.userId, } : null, ), ); </script> <template> <div v-if="postError">Error {{ postError.status }}</div> <Loading v-else-if="postLoading || !post" /> <div v-else-if="authorError">Error {{ authorError.status }}</div> <Loading v-else-if="authorLoading || !author" /> <div v-else>{{ author.username }}</div> </template>
Embedded data
When entities are stored in nested structures, that structure will remain.
▶api/Post
export class PaginatedPost extends Entity { id = ''; title = ''; content = ''; static key = 'PaginatedPost'; } export const getPosts = new RestEndpoint({ path: '/post', searchParams: { page: '' }, schema: { results: new Collection([PaginatedPost]), nextPage: '', lastPage: '', }, });
▶ArticleList.vue
<script setup lang="ts"> import { useDLE } from '@data-client/vue'; import { getPosts } from './api/Post'; const props = defineProps<{ page: string }>(); const { data, loading, error } = useDLE(getPosts, { page: props.page }); </script> <template> <div v-if="error">Error {{ error.status }}</div> <Loading v-else-if="loading || !data" /> <div v-else> <div v-for="post in data.results" :key="post.pk()"> {{ post.title }} </div> </div> </template>