useDLE() - Estado con [D]ata [L]oading [E]rror
Renderizado de datos asíncronos de alto rendimiento sin sobrecargar las solicitudes. Con metadatos del fetch.
Si no puedes usar suspense, useDLE() es igual que useSuspense(), pero devuelve los valores [D]ata [L]oading [E]rror.
useDLE() reacciona a las mutaciones de datos; vuelve a renderizar solo cuando es necesario.
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>
Comportamiento
data, loading y error son cada uno un ComputedRef.
Desestructúralos en el nivel superior de <script setup> para que se desenvuelvan en la plantilla. La tabla
de abajo describe su .value.
| Caducidad | Fetch | Data | Loading | Error | Condiciones |
|---|---|---|---|---|---|
| Inválido | sí1 | undefined | true | false | no está en el store, eliminación, invalidación, invalidIfStale |
| Obsoleto | sí1 | desnormalizado | false | false | (primer render, cambio de args) & caducidad < ahora |
| Válido | no | desnormalizado | false | quizás2 | fetch completado |
| no | undefined | false | false | null usado como segundo argumento |
- Los fetches idénticos se deduplican automáticamente
- Errores graves que deben ser capturados por onErrorCaptured()
Usar null como segundo argumento de cualquier hook de Data Client significa "no hacer 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>;
};
Los argumentos pueden ser valores simples, refs (incluidos los computed) o funciones getter
como () => ({ id: props.id }). Un objeto simple como { id: props.id } se lee una sola vez y no
seguirá los cambios de props ni de ruta, así que usa un getter o computed cuando un argumento pueda cambiar.
Los resultados se actualizan cuando cambian los argumentos.
Ejemplos
Detalle
<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 evitará enlazar y obtener datos
<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>
Datos incrustados
Cuando las entities se almacenan en estructuras anidadas, esa estructura se conservará.
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>