Renderizar mientras obtienes datos
Una característica de diseño central de Reactive Data Client es desacoplar la obtención real de los datos de su uso. Esto significa que los hooks que quieren asegurar la disponibilidad de datos, como useFetch() o useSuspense(), en realidad solo despachan la petición de fetch. NetworkManager usa entonces su conocimiento global para determinar si debe hacer el fetch. Esto significa, por ejemplo, que las peticiones duplicadas de datos se pueden deduplicar en un único fetch, con una sola promesa que resolver.
Otra implicación interesante es que los fetches iniciados de forma imperativa mediante Controller.fetchIfStale() y Controller.fetch() no provocarán fetches redundantes. Esto se conoce como 'fetch as you render' y a menudo da como resultado una mejor experiencia de usuario.
Estos son algunos escenarios donde este patrón es especialmente útil:
- Renderizado del lado del servidor
- Cargar datos en paralelo con el código
- Concurrent Mode
Fetch-as-you-render se puede adoptar de forma incremental. Los componentes que usan datos pueden usar useSuspense() y tener la seguridad de que recibirán sus datos cuando estén listos. Y cuando más adelante se añadan las optimizaciones de render-as-you-fetch, esos componentes no necesitan cambiar. Así el uso de datos queda estrechamente acoplado, y la optimización del fetch débilmente acoplada.
Rutas que precargan
En la mayoría de los casos, el mejor momento para precargar datos es en la capa de enrutamiento. Hacerlo hace muy fácil incorporar todas las capacidades anteriores.
Usa Controller.fetchIfStale en el manejador de eventos de la ruta (antes de startTransition)
import { Controller } from '@data-client/react';
import { lazy, Route } from '@anansi/router';
import { getImage } from '@data-client/img';
import { lazyPage } from './lazyPage';
import { PostResource } from '@/resources/Post';
import { UserResource } from '@/resources/User';
export const routes: Route<Controller>[] = [
{
name: 'UserDetail',
component: lazyPage('UserDetail'),
resolveData: async (controller: Controller, match: { id: string }) => {
if (match) {
const fakeUser = UserResource.fromJS({
id: Number.parseInt(match.id, 10),
});
// don't block on posts but start fetching
controller.fetchIfStale(PostResource.getList, { userId: match.id });
await Promise.all([
controller.fetchIfStale(UserResource.get, match),
controller.fetchIfStale(getImage, {
src: fakeUser.profileImage,
}),
controller.fetchIfStale(getImage, {
src: fakeUser.coverImage,
}),
controller.fetchIfStale(getImage, {
src: fakeUser.coverImageFallback,
}),
]);
}
},
},
];
Componentes que usan datos
import { useSuspense } from '@data-client/react';
import { Img } from '@data-client/img';
import { Card, Avatar } from 'antd';
import { UserResource } from 'resources/Discuss';
import Boundary from 'Boundary';
import PostList from 'pages/Posts';
export type Props = { id: string };
const { Meta } = Card;
export default function UserDetail({ id }: Props) {
const user = useSuspense(UserResource.get, { id });
return (
<>
<Card cover={<Img src={user.coverImage} />}>
<Meta
avatar={<Img component={Avatar} src={user.profileImage} size={64} />}
title={user.name}
description={
<>
<div>{user.website}</div>
<div>{user.company.catchPhrase}</div>
</>
}
/>
</Card>
<Boundary fallback={<CardLoading />}>
<PostList userId={user.pk()} />
</Boundary>
</>
);
}
export function CardLoading() {
return <Card style={{ marginTop: 16 }} loading={true} />;
}