Saltar al contenido principal

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.

CaducidadFetchDataLoadingErrorCondiciones
Inválidosí1undefinedtruefalseno está en el store, eliminación, invalidación, invalidIfStale
Obsoletosí1desnormalizadofalsefalse(primer render, cambio de args) & caducidad < ahora
Válidonodesnormalizadofalsequizás2fetch completado
noundefinedfalsefalsenull usado como segundo argumento
nota
  1. Los fetches idénticos se deduplican automáticamente
  2. Errores graves que deben ser capturados por onErrorCaptured()
Dependencias condicionales

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

▶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>

Datos incrustados​

Cuando las entities se almacenan en estructuras anidadas, esa estructura se conservará.

▶api/Post
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: '',
  },
});
▶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>