RTK Query: Multiple queries at once #1171
Replies: 20 comments 28 replies
-
This may not be 100% identical, but there was an SO question earlier today about an infinite scroll pagination scenario where it sounds like they want to keep showing existing data, but also fetch new data: |
Beta Was this translation helpful? Give feedback.
-
Yeah, Right now, you would probably need to do the logic by hand, meaning something like const runningQueries = useRef({})
useEffect(() => {
for (const [query, arg] of queries) {
if (!runningQueries.current[query]) {
runningQueries.current[query] = {}
}
if (!runningQueries.current[query][arg]) {
runningQueries.current[query][arg] = dispatch(api.endpoints[query].invoke(arg)
}
}
for (const [query, args] of Object.entries(runningQueries.current)) {
for (const [arg, promise] of Object.entries(args)) {
if (!queries.some(([q,a]) => q == query && a == arg) {
promise.unsubscribe()
}
}
}
}, queries) and then something similar with |
Beta Was this translation helpful? Give feedback.
-
For the "infinite scrolling" approach that Mark mentioned, I answered that over in StackOverflow. I don't think that will need a generalized API, as it is quite easy to implement with just three |
Beta Was this translation helpful? Give feedback.
-
Thanks a lot for your help. I really appreciate that. I'll try to incorporate your suggestions into our project tomorrow |
Beta Was this translation helpful? Give feedback.
-
@jonathanschad if you come up with fully runnable code, it would be great if you could share that here with others - mine above is pretty much pseudocode from the back of my head, so there's definitely things to improve on it :) |
Beta Was this translation helpful? Give feedback.
-
I have to admit defeat here. I tried converting your code in something usable but I just couldn't get it to work. Unfortunately, I don't have time to deal with this problem any further. I think for my case it should be enough to manually fetch the data. But thanks again for the help you guys provided :) |
Beta Was this translation helpful? Give feedback.
-
also faced the problem of implementing endless data loading and did not find an opportunity to implement it without crutches. very sad |
Beta Was this translation helpful? Give feedback.
-
@lamo2k123 hey, the library has only been out officially for just over a week :) If you can show us examples of the specific use case you're trying to solve, we can look at providing answers or improving the API, but just saying "can't do it without crutches, very sad" isn't a response that will help us help you. |
Beta Was this translation helpful? Give feedback.
-
Need an analogue useInfiniteQuery https://react-query.tanstack.com/guides/infinite-queries I saw that now there is TODO on Infinite scrolling. Upset by the absence of Infinite scrolling Thanks for what you are doing and for developing the tools. |
Beta Was this translation helpful? Give feedback.
-
@lamo2k123 Lenz showed one possible approach for an infinite query implementation in that Stack Overflow answer: Does something like that work for you? |
Beta Was this translation helpful? Give feedback.
-
This option is not suitable in my case. I need an honest list of what has been uploaded |
Beta Was this translation helpful? Give feedback.
-
@lamo2k123 can you give more specifics? what do you mean by an "honest list"? The more details you can provide, the better we can understand what you're trying to do and what might be helpful. Having said that, I'm going to go ahead and convert this into a discussion thread, which I think will work better. |
Beta Was this translation helpful? Give feedback.
-
We should probably remove that "TODO" and add an "X" or something there. At the current point, I'm more inclined to collect several cases and add recipes for them in the docs. From all the experiences I've had with infinite fetching "solutions" of other libraries, they work very good for some cases and are an absolute pain in other cases. I'd rather have some primitives in place and documented use cases than having something that only works in a few very select cases. |
Beta Was this translation helpful? Give feedback.
-
I was wondering if there were any updates on the progress of the infinit scrolling? I do have a custom solution, but honestly wished RTK Query supported it out of the box. |
Beta Was this translation helpful? Give feedback.
-
Hi, |
Beta Was this translation helpful? Give feedback.
-
Hello everyone! I was looking into this issue and I managed a workaround but it is far from ideal. Disclaimer: I am very new to Redux. PremiseFor my use case, I have a list of query arguments, say a list of Pokemon names. const pokemon = ["pikachu", "zapdos", "squirtle"]; For each name, I would like to query an API, say one that gives the Pokemon's type along with other information. A few caveats:
You can imagine a scenario where there are many different Pokemon names (around 5-7). WorkaroundI call useEffect(() => {
pokemon.forEach((p) => {
const result = dispatch(
pokeAPI.endpoints.getType.initiate({
name: p,
})
);
result.unsubscribe();
});
}, [pokemon, dispatch]); I then call const selectAllPokemonType = (state, names) =>
names.map((n) => pokeAPI.endpoints.getType.select({ name: n })(state));
// inside a component
// ...
const allPokemonType = useSelector((state) =>
selectAllPokemonType(state, pokemon)
);
// compute if any pokemon share a type
// ... While this works, it updates ConclusionI would really appreciate some feedback/advice. I would also appreciate an update on the official implementation of Thank you very much for your time and for actively maintaining this awesome project. |
Beta Was this translation helpful? Give feedback.
-
Here is a snippet I used for search pagination. Hope this helps someone. Should probably make a PR to bake this into RTK at some point. Usage const queries = useMemo(() => {
return pageTokens.map((pageToken) => ({
...baseQuery,
lastKey: pageToken,
}))
}, [baseQuery, pageTokens])
const responses = useQueries(myApi.endpoints.search, queries) Hook import { useEffect, useMemo, useReducer, useRef } from 'react'
import { useDispatch } from 'react-redux'
function loadingReducer(_state, originalArgs) {
return {
isUninitialized: false,
isLoading: true,
isFetching: true,
isSuccess: false,
isError: false,
originalArgs: originalArgs,
data: undefined,
currentData: undefined,
error: undefined,
}
}
function fetchingReducer(state, originalArgs) {
return {
isUninitialized: false,
isLoading: state.data === undefined,
isFetching: true,
isSuccess: state.data !== undefined,
isError: false,
originalArgs: originalArgs,
data: state.data,
currentData: undefined,
error: undefined,
}
}
function successReducer(state, data) {
return {
isUninitialized: false,
isLoading: false,
isFetching: false,
isSuccess: true,
isError: false,
originalArgs: state.originalArgs,
data,
currentData: data,
error: undefined,
}
}
function errorReducer(state, error) {
return {
isUninitialized: false,
isLoading: false,
isFetching: false,
isSuccess: false,
isError: true,
originalArgs: state.originalArgs,
data: state.data,
currentData: undefined,
error,
}
}
function useQueryResult(originalArgs) {
const [state, setState] = useReducer(
(state, [reducer, value]) => reducer(state, value),
undefined,
() => loadingReducer(undefined, originalArgs),
)
const setStateWrapper = useMemo(
() => ({
loading(originalArgs) {
setState([loadingReducer, originalArgs])
},
fetching(originalArgs) {
setState([fetchingReducer, originalArgs])
},
success(data) {
setState([successReducer, data])
},
error(error) {
setState([errorReducer, error])
},
}),
[],
)
return [state, setStateWrapper]
}
export default function useQueries(endpoint, originalArgs = [undefined]) {
const endpointRef = useRef()
const dispatch = useDispatch()
const [queryResult, setQueryResult] = useQueryResult(originalArgs)
useEffect(() => {
let active = true
const actions = originalArgs.map((originalArg) =>
endpoint.initiate(originalArg),
)
const results = actions.map((action) => dispatch(action))
const unwrappedResults = results.map((result) => result.unwrap())
if (endpointRef.current !== endpoint) {
endpointRef.current = endpoint
setQueryResult.loading(originalArgs)
} else {
setQueryResult.fetching(originalArgs)
}
Promise.all(unwrappedResults)
.then((responses) => {
if (active) {
setQueryResult.success(responses)
}
})
.catch((errResponse) => {
if (active) {
setQueryResult.error(errResponse)
}
})
return () => {
active = false
results.forEach((result) => {
result.unsubscribe()
})
}
}, [endpoint, originalArgs, dispatch, setQueryResult])
return queryResult
} |
Beta Was this translation helpful? Give feedback.
-
Hey! I'm running into a similar issue but not in the context of infinite scrolling, which is what most of this discussion seems to be about. My use case is a simple N+1 query*—actually, two of them:
This populates an Our application uses React with Redux Toolkit, but we don't use RTK Query for these endpoints. But we miss the ergonomics, the consistent caching behavior, and the nice encapsulation of the request machinery. Is this a pattern that RTK Query can comfortably support and a use case that you would recommend? * I understand that this interface might raise some eyebrows from a performance design standpoint for traditional HTTP APIs, but in this case we're talking over a low-latency WebRTC data channel where the client can easily be updated but the server can't. We're comfortable sacrificing some wire inefficiency so that the server API can be simple and decoupled from its clients. |
Beta Was this translation helpful? Give feedback.
-
still not working multiple query at once |
Beta Was this translation helpful? Give feedback.
-
Coming back to this since I now want it again for infinite scroll reasons. I looked at the solution in the Stack Overflow post by @phryneas, but I don't see how to adapt it to a pagination API that is uses cursors rather than page numbers (since page-number-based APIs tend to run into performance issues). My API looks like this: interface ListRequest {
after?: string; // opaque cursor
}
interface ListResponse {
items: T[];
hasNextPage: boolean;
endCursor?: string;
}
// analogous to the Relay cursor connections spec, though I don't use Relay:
// https://relay.dev/graphql/connections.htm
// usage:
list()
==> { items: [t1, t2], hasNextPage: true, endCursor: "xER0A41FnkDkcUr-" }
list({ after: "xER0A41FnkDkcUr-" })
==> { items: [t3, t4], hasNextPage: true, endCursor: "ovGmijz3urFCRbo0" }
list({ after: "ovGmijz3urFCRbo0" })
==> { items: [t5], hasNextPage: false })
result = [t1, t2, t3, t4, t5] The issue is that we can't map from a scroll position to a cursor, nor is there an analogue of " I've also seen this attempt on Stack Overflow, which uses the |
Beta Was this translation helpful? Give feedback.
-
Today I played around a bit with RTK Query and I was wondering if there is a way to create multiple queries at once.
The usecase for our application is the following: We have a list of larger documents(could be easily < 10mb per document) that we dynamically load from an API. These documents are needed in many different places in the application. However, the length of the list is dynamic, which means we don't know how many API requests need to be sent at the time of writing the code. Also combining the individual requests into one large request is unfortunately not possible due to the size of the data.
React-Query offers a seemingly suitable solution for this with the hook useQueries.
Another possibility we have thought about would be to write the loaded data separately into the reduxstore and then subscribe to it using useSelector. However, this does not seem to me to be an acceptable solution, as it also unnecessarily inflates the store.
Hence my question if RTK Query also offers a solution for this problem or what would be the best approach in this case?
Beta Was this translation helpful? Give feedback.
All reactions