redux-toolkit
redux-toolkit copied to clipboard
RTKQ - Hook for array of queries
Hi All!
I was wondering if there was a good way to handle an array of queries that change from one render to the next? This issue on react-query
describes the problem quite well:
https://github.com/tannerlinsley/react-query/issues/138
this resulted in the new hook useQueries
:
https://react-query.tanstack.com/reference/useQueries
This seems to be a very natural use case and until now I worked around it with a custom queryFn
but of course caching does not behave since ideally I would have a cached value for every individual object instead of a value for the request array.
I could not find something that would allow us to do what is achieved by react-query
with the useQueries
hook, any insight?
It's pretty much possible on the Redux side but you'd have to write your own hook for it at the moment. The current hook implementation is here: https://github.com/reduxjs/redux-toolkit/blob/ed75ee07c4116ea22cb69a52d55acee5c4991054/packages/toolkit/src/query/react/buildHooks.ts#L493
I guess we'll consider it in a future release, but right now there are higher prioritized items on the list.
Thanks for the pointer! However I am not sure this would give me the desired caching behaviour. For example if I first request objects with ids [1,2,3]
and then later on objects with ids [3,4,5]
I would like object 3
to come from the cache. I'll probably use the entity adapter for now that allows this use case quite nicely, just was curious if there was something I was missing with RTKQ.
For example if I first request objects with ids [1,2,3] and then later on objects with ids [3,4,5] I would like object 3 to come from the cache.
Assuming you mean three requests for 1,2,3 each and later three requests for 3,4,5 each, that's not a problem - initiate
might fire, but it would not make a request since there is already data in the cache.
Yes thanks, makes total sense, I was indeed missing something 😄
@nicolas-chaulet Did you manage to work something out? I have similar needs for a project of my own, but haven't been successful in creating a custom hook for this yet...
Hey! I ended up implementing my own slice starting from an entity adapter, I can easily normalise my data and that was actually quite simple to implement.
For anyone coming to this issue I made a hook for a single query. I don't know if this is can generalized to work with any query.
// useGetCurrencyRateMultipleQueries.ts
export const useGetCurrencyRateMultipleQueries = (
options: (GetCurrencyRatesOptions | SkipToken)[]
) => {
const dispatch = useAppDispatch();
useEffect(() => {
const subscriptions = options
.filter((options): options is GetCurrencyRatesOptions => {
return options !== skipToken;
})
.map((options) => {
return dispatch(exopenApi.endpoints.getCurrencyRates.initiate(options));
});
return () => {
for (const subscription of subscriptions) {
subscription.unsubscribe();
}
};
}, [dispatch, options]);
return useAppSelector((state) => {
return options.map((options) => {
return exopenApi.endpoints.getCurrencyRates.select(options)(state);
});
});
};
const options = useMemo(() => {
return filteredExchangeStrategies.map((strategy) => {
if (!exchangeStrategies) {
return skipToken;
}
return {
from: strategy.fromCurrency,
to: exchangeStrategies.toCurrency,
periods: [periodString],
provider: strategy.rateProvider,
type: strategy.rateType,
};
});
}, [exchangeStrategies, filteredExchangeStrategies, periodString]);
const currencyRates = useGetCurrencyRateMultipleQueries(options);
I tried my hand at generalizing @hornta's solution, but for type safety it seemed to require some pretty dirty imports, maybe someone knows how to clean the type signatures a bit?
import { ThunkDispatch } from '@reduxjs/toolkit';
import { QueryDefinition } from '@reduxjs/toolkit/dist/query';
import { QueryActionCreatorResult } from '@reduxjs/toolkit/dist/query/core/buildInitiate';
import { ApiEndpointQuery } from '@reduxjs/toolkit/dist/query/core/module';
export function useQueries<QueryArgs, ReturnValue>(
endpointQuery: ApiEndpointQuery<
QueryDefinition<QueryArgs, any, any, ReturnValue, string>,
any
>
) {
return function (options: (QueryArgs | SkipToken)[]) {
const dispatch = useAppDispatch<ThunkDispatch<any, any, AnyAction>>();
useEffect(() => {
const subscriptions: QueryActionCreatorResult<any>[] = options
.filter((options): options is QueryArgs => options !== skipToken)
.map((options) => dispatch(endpointQuery.initiate(options)));
return () => {
subscriptions.forEach((s) => s.unsubscribe());
};
}, [dispatch, options]);
return useAppSelector((state) =>
options.map((options) => endpointQuery.select(options)(state))
);
};
}
But with this you can easily define more than one multi-query with properly inferred types:
const useGetDogsQueries = useQueries(api.endpoints.getDogs);
const useGetCatsQueries = useQueries(otherApi.endpoints.getCats);
const dogs = useGetDogsQueries(options);
const cats = useGetCatsQueries(options);
@Jonesus In this discussion phryneas linked to a PR with an initial implementation of a multi query hook https://github.com/reduxjs/redux-toolkit/discussions/2398#discussioncomment-2925776
I've not yet tested that branch but maybe you can look into that as well and see if you can help.