Renderizando dados assíncronos
Torne seus componentes reutilizáveis associando os dados onde você os usa, com o useSuspense() de uma linha, que garante os dados como await.
import { useSuspense } from '@data-client/react'; import PostItem from './PostItem'; import { PostResource } from './Resources'; export default function PostList({ setRoute }) { const posts = useSuspense(PostResource.getList); return ( <div> {posts.map(post => ( <PostItem key={post.pk()} post={post} setRoute={setRoute} /> ))} </div> ); }

Não faça prop drilling. Em vez disso, use useSuspense() nos componentes que renderizam os dados. Isso é conhecido como data co-location (colocalização de dados).
Não esconda hooks de associação de dados dentro de hooks personalizados. Em vez disso, coloque transformações de dados fortemente acopladas em Query: a lógica de dados pertence ao modelo de dados, onde permanece visível, reutilizável e livre para mudar independentemente da view.
Em vez de escrever funções de atualização complexas ou cascatas de invalidações, o Reactive Data Client atualiza automaticamente os componentes associados imediatamente após uma alteração nos dados. Isso é conhecido como programação reativa.
Carregamento e erro
Você pode ter notado que o tipo de retorno mostra que o valor sempre está presente. useSuspense() funciona de forma muito parecida com await. Isso nos permite separar o tratamento de erro/carregamento do uso dos dados.
Limites assíncronos (Async Boundaries)
Em vez disso, colocamos <AsyncBoundary /> para tratar as condições de carregamento e de erro em limites de navegação, ou acima deles, como páginas, rotas ou modais.
- React Router
- NextJS
- Expo
- Antd Modal
import { AsyncBoundary } from '@data-client/react';
import { Outlet } from 'react-router';
export default function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<section>
<AsyncBoundary>
<Outlet />
</AsyncBoundary>
</section>
</div>
);
}
import { AsyncBoundary } from '@data-client/react';
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<h1>Dashboard</h1>
<section>
<AsyncBoundary>{children}</AsyncBoundary>
</section>
</div>
);
}
import { AsyncBoundary } from '@data-client/react';
import { Slot } from 'expo-router';
import { Image, StyleSheet } from 'react-native';
import ParallaxScrollView from '@/components/ParallaxScrollView';
export default function DashboardLayout() {
return (
<ParallaxScrollView
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
headerImage={
<Image
source={require('@/assets/images/my-logo.png')}
style={styles.logo}
/>
}
>
<AsyncBoundary>
<Slot />
</AsyncBoundary>
</ParallaxScrollView>
);
}
const styles = StyleSheet.create({
logo: { height: 178, width: 290 },
});
import { AsyncBoundary } from '@data-client/react';
import { useState } from 'react';
import { Button, Modal } from 'antd';
import MyModalBody from './MyModalBody';
export default function ModalOpen() {
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => setIsModalOpen(true);
const handleOk = () => setIsModalOpen(false);
const handleCancel = () => setIsModalOpen(false);
return (
<>
<Button type="primary" onClick={showModal}>
Open Modal
</Button>
<Modal title="Basic Modal" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
<AsyncBoundary>
<MyModalBody />
</AsyncBoundary>
</Modal>
</>
);
}
O useTransition do React 18 e roteadores ou navegação baseados em Server Side Rendering significam nunca mais ver um fallback de carregamento. No React 16 e 17, os fallbacks podem ser centralizados para eliminar indicadores de carregamento redundantes, mantendo os componentes reutilizáveis.
<AsyncBoundary /> também permite que o Server Side Rendering transmita o HTML de forma incremental (streaming), reduzindo bastante o TTFB. A hidratação automática do store do SSR do Reactive Data Client significa interatividade imediata para o usuário com zero fetches no cliente no primeiro carregamento.
O fallback de erro e o fallback de carregamento do AsyncBoundary podem ser ambos personalizados.
Com estado (Stateful)
Você pode encontrar casos em que ainda é útil usar uma abordagem com estado para os fallbacks ao usar React 16 e 17. Para esses casos, ou para compatibilidade com algumas bibliotecas de componentes, há o useDLE() - [D]ata [L]oading [E]rror (dados, carregamento e erro).
import React from 'react'; import { useDLE } from '@data-client/react'; import { ProfileResource } from './ProfileResource'; function ProfileList(): React.JSX.Element { const { data, loading, error } = useDLE(ProfileResource.getList); if (error) return <div>Error {`${error.status}`}</div>; if (loading || !data) return <Loading />; return ( <div> {data.map(profile => ( <div className="listItem" key={profile.pk()}> <Avatar src={profile.avatar} /> <div> <h4>{profile.fullName}</h4> <p>{profile.bio}</p> </div> </div> ))} </div> ); } render(<ProfileList />);
Como useDLE não usa useSuspense, você não conseguirá orquestrar facilmente de forma centralizada o código de carregamento e de erro. Além disso, recursos do React 18, como useTransition e SSR com streaming incremental, não funcionarão com componentes que o utilizam.
Condicional
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 = useSuspense(TodoResource.get, id ? { id } : null);
Subscriptions
Quando é provável que os dados mudem por fatores externos, useSubscription() garante atualizações contínuas enquanto um componente está montado. useLive() chama tanto useSubscription() quanto useSuspense(), facilitando bastante o uso de dados atualizados.
import { useLive } from '@data-client/react'; import NumberFlow from '@number-flow/react'; import { getTicker } from './Ticker'; function AssetPrice({ productId }: Props) { const ticker = useLive(getTicker, { productId }); return ( <center> {productId}{' '} <NumberFlow value={ticker.price} format={{ style: 'currency', currency: 'USD' }} /> </center> ); } interface Props { productId: string; } render(<AssetPrice productId="BTC-USD" />);
As subscriptions são orquestradas por Managers. Por padrão, subscriptions baseadas em polling podem ser usadas adicionando pollFrequency a um Endpoint ou Resource. Para protocolos de rede baseados em push, como SSE e websockets, veja o exemplo de manager de stream.
export const getTicker = new RestEndpoint({
urlPrefix: 'https://api.exchange.coinbase.com',
path: '/products/:productId/ticker',
schema: Ticker,
pollFrequency: 2000,
});