redux-toolkit
redux-toolkit copied to clipboard
feature request: TypedUseQuery
typing builder.query
is easy like
builder.query<ReturnType, ArgType>
and have used it to query blockchain LCD endpoints
queryA - builder.query<A, Z>
queryB - builder.query<B, Z>
queryC - builder.query<C, Z>
queryD - builder.query<D, Z>
type Z = {
msg: object
contract: string
}
because queries A
,B
,C
,D
have the same query implementation,
query({contract, msg}) => `lcdEndpoint/${contract}/${toBase64(msg)}`
transformResponse: (res: { data: T }) => {
return res.data
}
I tried to wrap the generic useQuery
hook with something like useGenericQuery
see at code sandbox
import { BaseQueryFn } from "@reduxjs/toolkit/dist/query";
import { QueryDefinition } from "@reduxjs/toolkit/dist/query/endpointDefinitions";
import { UseQuery } from "@reduxjs/toolkit/dist/query/react/buildHooks";
type Queries = {
queryA: { args: { id: number }; result: ResultA };
queryB: { args: { addr: string }; result: ResultB };
queryC: { args: null; result: ResultC };
};
type Q = keyof Queries;
type Base = BaseQueryFn<any, unknown, unknown, {}, {}>;
type GenericUseQuery<T extends Q> = UseQuery<
QueryDefinition<Queries[T]["args"], Base, string, Queries[T]["result"], "">
>;
type GenericResult<T extends Q> = TypedUseQueryHookResult<
Queries[T]["result"],
Queries[T]["args"],
Base
>;
export function useGenericQuery<
T extends Q,
K extends Parameters<GenericUseQuery<T>>
>(type: T, contract: string, args: K[0], options?: K[1]) {
return useQuery(
{
type,
contract,
msg: { balance: {} },
},
options
) as GenericResult<T>;
}
so that instead of using different endpoints
//ComponentA.tsx
const resultA /** ResultA */ = useQueryA(argsA);
//ComponentB.tsx
const resultB /** ResultB */ = useQueryB(argsB);
//ComponentC.tsx
const resultC /** ResultC */ = useQueryC(argsC);
I could just use one
//ComponentA.tsx
const genericResult /** ResultA */ = useGenericQuery(type:"queryA", {/** ArgsA*/});
//ComponentB.tsx
const genericResult /** ResultB */ = useGenericQuery(type:"queryB", {/** ArgsB*/});
//ComponentC.tsx
const genericResult /** ResultC */ = useGenericQuery(type:"queryC", {/** ArgsC*/});
I had initial success with this approach, but I encountered some difference
from just using useQuery
as it is
see at code sandbox
-
data
is not typed according to return ofselectFromResult
,
type ResultA = {
{ results: string[] }
}
const { data /** must be string[], but ResultA instead*/ } = useGenericQuery("queryA", "contract_address", {}, {
selectFromResult: ({ data /** ResultA */}) => ({
data: data.results, // string[]
}),
})
-
data
doesn't follow name ofselectFromResult
type ResultA = {
{ results: string[] }
}
const { renamedResult /** error, should be 'data' not 'renamedResult' */ } = useGenericQuery("queryA", "contract_address", {}, {
selectFromResult: ({ data /** ResultA */}) => ({
renamedResult: data.results,
}),
})
some help would be really appreciated to iron out these bumps
something like TypedUseQuery
would be a life saver!
@ap-justin we actually literally just shipped a TypedUseQueryHookResult
type in 1.8.4 :)
https://github.com/reduxjs/redux-toolkit/releases/tag/v1.8.4
@markerikson, I had success on the result
side of things with
type GenericResult<T extends Q> = TypedUseQueryHookResult<
Queries[T]["result"],
Queries[T]["args"],
Base
>;
my problem now is dealing with the options
, particularly selectFromResult
type ResultA = {
{ results: string[] }
}
const { data /** must be string[], but ResultA instead*/ } = useGenericQuery("queryA", "contract_address", {}, {
selectFromResult: ({ data /** ResultA */}) => ({
data: data.results, // string[]
}),
})
kindly see at code sandbox