I'd like to make a generic hook that takes useQuery
as a parameter and handles pagination.
That means my trpc query will always provide a skip
and a take
parameter. And it will always return a count
in the response and an array of items
.
function usePagination(query){
const [skip, take] = useSomethingThatManagesThis();
const response = query.useQuery({
skip,
take,
});
return useMemo(() => ({
items: response.data?.items,
count: response.data?.count
}), [response]);
}
function MyComponent() {
const paginated = usePagination(api.user.list);
const firstUserEmail = paginated.items?.[0].email;
}
It works in vanilla javascript, but I'm struggling to type it correctly.
I tried to naively reuse trpc client types, but I'm not able to distinguish request type (take, query) and response type (items).
import {
AnyProcedure,
AnyQueryProcedure,
inferProcedureOutput,
} from "@trpc/server";
interface BaseInput {
skip: number;
take: number;
}
type UseDatatableArgs<TData extends AnyQueryProcedure> = {
query?: {
useQuery: (input: BaseInput) => ProcedureUseQuery<
{ count: number } & AnyProcedure,
string
> & {
data: {
count: number;
items: inferProcedureOutput<TData>;
};
};
};
};
function usePagination<TData extends AnyQueryProcedure>(query){
const [skip, take] = useSomethingThatManagesThis();
const response = query.useQuery({
skip,
take,
});
return useMemo(() => ({
items: response.data?.items,
count: response.data?.count
}), [response]);
}
But I'm having no items typing in the response
function MyComponent() {
const paginated = usePagination(api.user.list);
const firstUserEmail = paginated.items?.[0].email; // paginated.items is any
}
I'm currently having a workaround by using types inferring utilities provided by TRPC, but I'd love to deduct that from the query
export type RouterOutputs = inferRouterOutputs<AppRouter>;
function MyComponent() {
const paginated = usePagination<RouterOutputs["user"]["list"]>(api.user.list);
const firstUserEmail = paginated.items?.[0].email;
}