Pular para o conteúdo principal

useQuery()

Renderização de dados sem o fetch.

Acesse o valor no store de qualquer Schema Queryable, como Entity, All, Collection, Query, Union e Scalar. Campos Lazy também funcionam por meio do accessor .query. Se o valor não existir, retorna undefined.

useQuery() é reativo às mutações de dados, renderizando novamente apenas quando necessário. Retorna undefined quando os dados são Inválidos.

dica

Queries são ótimas companheiras para renderizar com eficiência cálculos agregados, como os que usam groupBy, map, reduce e filter.

Uso​

<script setup lang="ts">
  import { Query } from '@data-client/rest';
  import { useQuery } from '@data-client/vue';
  import { PostResource } from './PostResource';

  const queryTotalVotes = new Query(
    PostResource.getList.schema,
    posts => posts.reduce((total, post) => total + post.votes, 0),
  );

  const props = defineProps<{ userId: number }>();
  const totalVotes = useQuery(queryTotalVotes, () => ({ userId: props.userId }));
</script>

<template>
  <div style="text-align: center">
    <small>{{ totalVotes }} votes total</small>
  </div>
</template>

Veja truthiness narrowing para mais informações sobre o tratamento de tipos

Tipos​

function useQuery<S extends Queryable>(
schema: S,
...args: MaybeRefsOrGetters<SchemaArgs<S>>
): ComputedRef<DenormalizeNullable<S> | undefined>;

Os argumentos podem ser valores simples, refs (incluindo computed) ou funções getter como () => ({ id: props.id }). Um objeto simples como { id: props.id } é lido uma única vez e não acompanha mudanças de props ou de rota, então use um getter ou computed quando um argumento puder mudar.

O resultado é atualizado quando os argumentos mudam.

Queryable​

Schemas Queryable exigem um método queryKey() que retorne algo. Isso inclui Entity, All, Collection, Query, Union e Scalar. Campos Lazy produzem um Queryable por meio do accessor .query.

interface Queryable {
queryKey(
args: readonly any[],
queryKey: (...args: any) => any,
getEntity: GetEntity,
getIndex: GetIndex,
// Must be non-void
): {};
}

Exemplos​

Ordenação e filtragem​

Query fornece acesso programático ao store do Reactive Data Client.

<script setup lang="ts">
  import { Query, All } from '@data-client/rest';
  import { useQuery, useFetch } from '@data-client/vue';
  import { UserResource, User } from './UserResource';

  interface Args {
    asc: boolean;
    isAdmin?: boolean;
  }
  const sortedUsers = new Query(
    new All(User),
    (entries, { asc, isAdmin }: Args = { asc: false }) => {
      let sorted = [...entries].sort((a, b) =>
        a.name.localeCompare(b.name),
      );
      if (isAdmin !== undefined)
        sorted = sorted.filter(user => user.isAdmin === isAdmin);
      if (asc) return sorted;
      return sorted.reverse();
    },
  );

  useFetch(UserResource.getList);
  const users = useQuery(sortedUsers, { asc: true });
</script>

<template>
  <div v-if="!users">No users in cache yet</div>
  <div v-else>
    <div v-for="user in users" :key="user.pk()">{{ user.name }}</div>
  </div>
</template>

Relacionamentos Lazy​

Campos Lazy mantêm os IDs brutos durante a desnormalização do pai. Use .query com useQuery para resolvê-los sob demanda, isolando as renderizações apenas aos componentes que precisam dos dados relacionados.

<script setup lang="ts">
  import { computed } from 'vue';
  import { useQuery } from '@data-client/vue';
  import { Department } from './Resources';

  const props = defineProps<{ dept: Department }>();

  const buildings = useQuery(
    Department.schema.buildings.query,
    computed(() => props.dept.buildings),
  );
</script>

<template>
  <span v-if="buildings">{{ buildings.map(b => b.name).join(', ') }}</span>
</template>
<script setup lang="ts">
  import { All } from '@data-client/rest';
  import { useQuery, useFetch } from '@data-client/vue';
  import { DepartmentResource, Department } from './Resources';
  import BuildingList from './BuildingList.vue';

  useFetch(DepartmentResource.getList);
  const departments = useQuery(new All(Department));
</script>

<template>
  <div v-if="!departments">Loading...</div>
  <div v-else>
    <div v-for="dept in departments" :key="dept.pk()">
      <strong>{{ dept.name }}</strong>: <BuildingList :dept="dept" />
    </div>
  </div>
</template>