useDLE() - [D]ata, [L]oading e [E]rror (dados, carregamento e erro)
Renderização de dados assíncronos de alto desempenho, sem overfetching. Com metadados do fetch.
Caso você não possa usar suspense, useDLE() é igual ao useSuspense(), mas retorna os valores [D]ata [L]oading [E]rror.
useDLE() reage às mutações de dados, renderizando novamente somente quando necessário.
Uso
<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>
Comportamento
data, loading e error são, cada um, um ComputedRef.
Desestruture-os no nível superior do <script setup> para que sejam desembrulhados no template. A tabela
abaixo descreve o .value deles.
| Status de expiração | Fetch | Data | Loading | Error | Condições |
|---|---|---|---|---|---|
| Inválido | sim1 | undefined | true | false | não está na store, exclusão, invalidação, invalidIfStale |
| Desatualizado | sim1 | desnormalizado | false | false | (primeira renderização, mudança de args) & expiração < agora |
| Válido | não | desnormalizado | false | talvez2 | conclusão do fetch |
| não | undefined | false | false | null usado como segundo argumento |
- Fetches idênticos são automaticamente deduplicados
- Erros hard devem ser capturados por onErrorCaptured()
Usar null como segundo argumento de qualquer hook do Data Client significa "não fazer nada."
// todo could be undefined if id is undefined
const todo = useDLE(
TodoResource.get,
computed(() => (id.value ? { id: id.value } : null)),
);
Tipos
function useDLE(
endpoint: ReadEndpoint,
...args: MaybeRefsOrGetters<Parameters<typeof endpoint>> | [null]
): {
data: ComputedRef<DenormalizeNullable<typeof endpoint.schema>>;
loading: ComputedRef<boolean>;
error: ComputedRef<ErrorTypes | undefined>;
};
Os argumentos podem ser valores simples, refs (incluindo computed) ou funções getter
como () => ({ id: props.id }). Um objeto simples como { id: props.id } é lido uma única vez e não
acompanha mudanças de props ou de rota, então use um getter ou computed quando um argumento puder mudar.
Os resultados são atualizados quando os argumentos mudam.
Exemplos
Detalhe
<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>
Condicional
null evita vincular e buscar os dados
<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>
Dados incorporados
Quando as entidades são armazenadas em estruturas aninhadas, essa estrutura é mantida.
import { Entity, RestEndpoint, Collection } from '@data-client/rest'; 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: '', }, });
<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>