Pular para o conteúdo principal

Query

Query fornece acesso programático ao cache do Reactive Data Client, mantendo o mesmo alto desempenho e as mesmas garantias de igualdade referencial esperados do Reactive Data Client.

Query pode ser renderizado usando o hook de consulta por schema useQuery()

Membros de Query​

schema​

Schema usado para obter/desnormalizar dados do cache do Reactive Data Client. Aceita qualquer schema Queryable: Entity, All, Collection, Query, Union, Scalar e schemas Object para fazer join de várias entities. Campos Lazy produzem um Queryable por meio do acessor .query.

process(entries, ...args)​

Recebe a resposta (desnormalizada) como entries, além dos argumentos, e retorna a nova resposta para uso com useQuery

Uso​

Mantendo a ordenação após criações​

import { Collection, Entity, Query, RestEndpoint } from '@data-client/rest';

export class Post extends Entity {
  id = '';
  title = '';
  group = '';
  author = '';
}

export const getPosts = new RestEndpoint({
  path: '/:group/posts',
  searchParams: {} as { orderBy?: string; author?: string },
  schema: new Query(
    new Collection([Post], {
      nonFilterArgumentKeys: /orderBy/,
    }),
    (posts, { orderBy } = {}) => {
      if (orderBy) {
        return [...posts].sort((a, b) =>
          a[orderBy].localeCompare(b[orderBy]),
        );
      }
      return posts;
    },
  ),
});
Resultado
Store▶

Agregados​

Fixtures
GET /users
[{"id":"123","name":"Jim"},{"id":"456","name":"Jane"},{"id":"777","name":"Albatras","isAdmin":true}]
▶resources/User
▶UsersPage
import { All, Query } from '@data-client/rest';
import { useQuery, useFetch } from '@data-client/react';
import { UserResource, User } from './resources/User';

const countUsers = new Query(
  new All(User),
  (entries, { isAdmin } = {}) => {
    if (isAdmin !== undefined)
      return entries.filter(user => user.isAdmin === isAdmin).length;
    return entries.length;
  },
);

function UsersPage() {
  useFetch(UserResource.getList);
  const userCount = useQuery(countUsers);
  const adminCount = useQuery(countUsers, { isAdmin: true });
  if (userCount === undefined) return <div>No users in cache yet</div>;
  return (
    <div>
      <div>Total users: {userCount}</div>
      <div>Total admins: {adminCount}</div>
    </div>
  );
}
render(<UsersPage />);
Resultado
Store▶

Reorganizando dados com agregações groupBy​

▶resources/User
▶resources/Todo
▶TodoByUser
▶TodoJoined
import { Query } from '@data-client/rest';
import { useQuery, useFetch, useSuspense } from '@data-client/react';
import { TodoResource } from './resources/Todo';
import { UserResource } from './resources/User';
import TodoByUser from './TodoByUser';

const groupTodoByUser = new Query(
  TodoResource.getList.schema,
  todos => Object.groupBy(todos, todo => todo.userId),
);

function TodosPage() {
  useFetch(UserResource.getList);
  useSuspense(TodoResource.getList);
  useSuspense(UserResource.getList);
  const todosByUser = useQuery(groupTodoByUser);
  if (!todosByUser) return <div>Todos not found</div>;
  return (
    <div>
      {Object.keys(todosByUser).slice(5).map(userId => (
        <TodoByUser
          key={userId}
          userId={userId}
          todos={todosByUser[userId]}
        />
      ))}
    </div>
  );
}
render(<TodosPage />);
Resultado
Store▶

Joins com Object Schema​

Query pode receber Object Schemas, permitindo joins entre vários tipos de entity. Isso permite combinar dados de diferentes entities em uma única query.

Fixtures
GET /tickers/BTC-USD
{"product_id":"BTC-USD","price":45000}
GET /stats/BTC-USD
{"product_id":"BTC-USD","last":44950}
▶resources/Ticker
▶resources/Stats
▶PriceDisplay
import { Query } from '@data-client/rest';
import { useQuery, useFetch } from '@data-client/react';
import { TickerResource, Ticker } from './resources/Ticker';
import { StatsResource, Stats } from './resources/Stats';

// Join Ticker and Stats by product_id
const queryPrice = new Query(
  { ticker: Ticker, stats: Stats },
  ({ ticker, stats }) => ticker?.price ?? stats?.last,
);

function PriceDisplay({ productId }: { productId: string }) {
  useFetch(TickerResource.get, { product_id: productId });
  useFetch(StatsResource.get, { product_id: productId });
  const price = useQuery(queryPrice, { product_id: productId });
  
  if (price === undefined) return <div>Loading...</div>;
  return <div>Price: ${price}</div>;
}

render(<PriceDisplay productId="BTC-USD" />);
Resultado
Store▶

Joins de fallback​

Neste caso, Ticker é atualizado constantemente a partir de um stream de websocket. No entanto, não há fetch em lote/lista para Ticker - o que torna ineficiente obter os preços em uma visualização de lista.

Portanto, neste caso podemos buscar uma lista de Stats como fallback, já que ela também tem dados de preço.

Explore o exemplo coin-app

More Demos