Renderizar datos asíncronos
Haz que tus componentes sean reutilizables vinculando los datos donde los usas con el useSuspense() de una sola línea, que garantiza los datos con await.
<script setup lang="ts"> import { useSuspense } from '@data-client/vue'; import PostItem from './PostItem.vue'; import { PostResource } from './Resources'; const emit = defineEmits<{ setRoute: [route: string] }>(); const posts = await useSuspense(PostResource.getList); </script> <template> <div> <PostItem v-for="post in posts" :key="post.pk()" :post="post" @setRoute="emit('setRoute', $event)" /> </div> </template>

No hagas prop drilling. En su lugar, usa useSuspense() en los componentes que renderizan los datos. Esto se conoce como colocación de datos (data co-location).
No ocultes los hooks de vinculación de datos dentro de hooks personalizados. En su lugar, coloca las transformaciones de datos estrechamente acopladas en un Query: la lógica de datos pertenece al modelo de datos, donde permanece visible, es reutilizable y puede cambiar de forma independiente de la vista.
En lugar de escribir complejas funciones de actualización o cascadas de invalidaciones, Reactive Data Client actualiza automáticamente los componentes vinculados de inmediato cuando los datos cambian. Esto se conoce como programación reactiva.
Carga y error
Es posible que hayas notado que el tipo de retorno indica que el valor siempre está presente. useSuspense() funciona de forma muy parecida con await. Esto nos permite separar el manejo de errores y de carga del uso de los datos.
Async Boundaries
En su lugar, colocamos el <Suspense /> integrado de Vue junto con onErrorCaptured() para manejar las condiciones de carga y error en o por encima de los límites de navegación, como páginas, rutas o modales.
<script setup lang="ts">
import { onErrorCaptured, ref } from 'vue';
const error = ref<Error | null>(null);
onErrorCaptured(err => {
error.value = err;
return false;
});
</script>
<template>
<div>
<h1>Dashboard</h1>
<section>
<div v-if="error">Error: {{ error.message }}</div>
<Suspense v-else>
<template #default>
<RouterView />
</template>
<template #fallback>
<Loading />
</template>
</Suspense>
</section>
</div>
</template>
Centralizar los fallbacks de esta manera elimina indicadores de carga redundantes y mantiene los componentes reutilizables.
El fallback de carga se personaliza con el slot #fallback de <Suspense />,
y el fallback de error, renderizando lo que elijas desde onErrorCaptured().
Con estado
Puede haber casos en los que siga siendo útil un enfoque con estado para los fallbacks. Para estos casos, o para la compatibilidad con algunas bibliotecas de componentes, se proporciona useDLE() - [D]ata [L]oading [E]rror (datos, carga, error).
<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>
Dado que useDLE no usa useSuspense, no podrás orquestar fácilmente de forma central el código de carga y de error .
Condicional
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 = await useSuspense(
TodoResource.get,
computed(() => (id.value ? { id: id.value } : null)),
);
Suscripciones
Cuando es probable que los datos cambien por factores externos, useSubscription() garantiza actualizaciones continuas mientras un componente está montado. useLive() llama tanto a useSubscription() como a useSuspense(), lo que hace muy fácil usar datos actualizados.
<script setup lang="ts"> import { useLive } from '@data-client/vue'; import { getTicker } from './Ticker'; import NumberFlow from '@number-flow/vue'; const props = defineProps<{ productId: string }>(); const ticker = await useLive(getTicker, () => ({ productId: props.productId })); </script> <template> <div style="text-align: center"> {{ productId }} <NumberFlow :value="ticker.price" :format="{ style: 'currency', currency: 'USD' }" /> </div> </template>
Las suscripciones son orquestadas por los Managers. De fábrica, se pueden usar suscripciones basadas en sondeo (polling) agregando pollFrequency a un Endpoint o Resource. Para protocolos de red basados en push, como SSE y websockets, consulta el ejemplo de stream manager.
export const getTicker = new RestEndpoint({
urlPrefix: 'https://api.exchange.coinbase.com',
path: '/products/:productId/ticker',
schema: Ticker,
pollFrequency: 2000,
});