useQuery()
Data rendering without the fetch.
Access any Queryable Schema's store value; like Entity, All, Collection, Query,
Union, and Scalar. Lazy fields also work via their .query accessor.
If the value does not exist, returns undefined.
useQuery() is reactive to data mutations; rerendering only when necessary. Returns undefined
when data is Invalid.
Usage
<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> <center> <small>{{ totalVotes }} votes total</small> </center> </template>
See truthiness narrowing for more information about type handling
Types
function useQuery<S extends Queryable>(
schema: S,
...args: MaybeRefsOrGetters<SchemaArgs<S>>
): ComputedRef<DenormalizeNullable<S> | undefined>;
Arguments can be plain values, refs (including computed), or getter
functions like () => ({ id: props.id }). A plain object like { id: props.id } is read once and won't
follow prop or route changes, so use a getter or computed when an argument can change.
The result updates when the arguments change.
Queryable
Queryable schemas require an queryKey() method that returns something. These include
Entity, All, Collection, Query,
Union, and Scalar. Lazy fields produce a Queryable via their .query accessor.
interface Queryable {
queryKey(
args: readonly any[],
queryKey: (...args: any) => any,
getEntity: GetEntity,
getIndex: GetIndex,
// Must be non-void
): {};
}
Examples
Sorting & Filtering
Query provides programmatic access to the Reactive Data Client store.
<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>
Lazy relationships
Lazy fields keep raw IDs during parent denormalization. Use .query with useQuery to resolve them on demand,
isolating re-renders to only the components that need the related data.
<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>