Pular para o conteúdo principal

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>
  );
}
Resultado
Store▶
Endpoints usados em muitos contextosEndpoints usados em muitos contextos

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.

Dashboard.tsx
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>
);
}

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 />);
Resultado
Store▶

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​

Dependências condicionais

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" />);
Resultado
Store▶

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,
});