T
TanStack•3y ago
national-gold

Running a query on an interval then updating the cache based on the response in an onSuccess

useQuery({
refetchInterval: 2000,
enabled: status === Status.PreparingUat,
queryKey: ["getUatStatus", id],
queryFn: () =>
functionThatReturnsUatStatus(id),
onSuccess: (status) => {
if (status === Status.UatInProgress) {
queryClient.setQueryData(["getCollection", id], (otherState) => ({ ...otherState, status }))
}
}
})
useQuery({
refetchInterval: 2000,
enabled: status === Status.PreparingUat,
queryKey: ["getUatStatus", id],
queryFn: () =>
functionThatReturnsUatStatus(id),
onSuccess: (status) => {
if (status === Status.UatInProgress) {
queryClient.setQueryData(["getCollection", id], (otherState) => ({ ...otherState, status }))
}
}
})
This seems to be the most straight forward way of doing this, but since onSuccess is deprecated, I'd like to make sure that it's the right thing to just use a useEffect? Also since I'm mutating the cache, a useMutation may make more sense, but then i need an interval
11 Replies
conscious-sapphire
conscious-sapphire•3y ago
I don't understand this code. What is it doing?
national-gold
national-goldOP•3y ago
every 2 seconds it is refreshing the UAT status (status of the object) Once that function -> functionThatReturnsUatStatus returns that the status is Status.UatInProgress, I can hide a loader bar but the interval is only enabled when the status is Status.PreparingUat
fascinating-indigo
fascinating-indigo•3y ago
The cache is updated automatically by useQuery so it looks like you can just remove onSuccess?
national-gold
national-goldOP•3y ago
Ah but unfortunately my simplification of this to ask the question is not 1:1 D: I updated the query client cache update Let me try to take a different approach: Expounded question: I have a main object that looks like the following:
type Status = "draft" | "preparing_uat" | "uat_in_progress" | "approved"
{
surveys: Survey[] // Type of Survey unimportant
languages: Language[] // Type of language unimportant
status: Status
}
type Status = "draft" | "preparing_uat" | "uat_in_progress" | "approved"
{
surveys: Survey[] // Type of Survey unimportant
languages: Language[] // Type of language unimportant
status: Status
}
I have a query fn that gets the entire object with the queryKey: ["getObject", objectId] Now that status changes from "draft" to "preparing_uat" then "preparing_uat" to "uat_in_progress" and when it does, it can take anywhere from 10-90 seconds, so we have to poll server to determine when that "preparing_uat" to "uat_in_progress" is done. While the status is in "preparing_uat", we display a loading bar, and poll the server ONLY for the object's status and NOT the languages/surveys as they don't need to be refreshed This is what I'm doing to poll right now:
useQuery({
refetchInterval: 2000,
enabled: status === "preparing_uat",
queryKey: ["getUatStatus", objectId],
queryFn: () => getUatStatus(objectId),
onSuccess: (data) => {
if (data.status === "uat_in_progress") {
queryClient.setQueryData(["getObject", objectId], (oldState) => ({
...oldState,
status: data.status,
}))
}
},
})
useQuery({
refetchInterval: 2000,
enabled: status === "preparing_uat",
queryKey: ["getUatStatus", objectId],
queryFn: () => getUatStatus(objectId),
onSuccess: (data) => {
if (data.status === "uat_in_progress") {
queryClient.setQueryData(["getObject", objectId], (oldState) => ({
...oldState,
status: data.status,
}))
}
},
})
This works great, but the problem is that onSuccess is getting deprecated, I considered using a useEffect, but I'm trying to figure out if i'm heading towards the wrong direction.
conscious-sapphire
conscious-sapphire•3y ago
refetchInterval can be a function that receives latest data for this case
national-gold
national-goldOP•3y ago
@TkDodo 🔮 so use refetchInterval as a function to update the query data? it's not a problem of stopping the interval but updating the global cache as a result of the interval.
conscious-sapphire
conscious-sapphire•3y ago
So your endpoint returns different structure depending on which state it is in?
fascinating-indigo
fascinating-indigo•3y ago
I think I understand: there are 2 endpoints: the first one returns an object that contains a status. The second one returns just the status. After calling the first one, we need to poll the second one until we get an updated status and when we do, we want to update the cache of the first one (hence onSuccess current being used). If it was possible to poll the first endpoint directly, that would probably simplify things (cache would just be automatically updated). Otherwise I might be wrong, but probably needs a useEffect?
national-gold
national-goldOP•3y ago
@julien this is exactly my situation So I'm considering looking at the fetchStatus to determine if the status is idle and previous status was fetching, and do the cache update in there
fascinating-indigo
fascinating-indigo•3y ago
Why not a useEffect with dependency data.status and update the cache inside? Or maybe just ignore the status field from the first query and always read the status from the second query (you can even pass the status from the first query as initial data to the second)
national-gold
national-goldOP•3y ago
ah right, good looks once that interval'd status is updated, update the main object's status. Thanks Julien! It does seem like this is an edge case where onSuccess is helpful and not an anti pattern, but I do agree @TkDodo 🔮 that 99% of the time, onSuccess is an anti pattern

Did you find this page helpful?