T
TanStack11mo ago
rare-sapphire

Pattern for mutations that trigger queries and further mutations

Hello all so using react query v4. Still fairly new to it and trying to figure out the best practices. I have a submit command that will need to do the following; 1. calls an api with a payload and that api runs an async task to update 2. polls another api to see status of the async job above 3. once poll completes with a status of success update another api with the payload 4. once that update is complete fetch data for other api's Code wise without reactQuery it'd be something like
const someApiCall = async(data) => {
// 1. calls an api with a payload and that api runs an async task to update
const {id} = creationApi(data)

// 2. polls another api to see status of the async job above
let isComplete = false;
if (!isComplete) {
const status = await apiToPoll(id)
if (status === 'error') {
throw new Error('goodbye world')
} else if (status !== 'inProgress') {
isComplete = true
} else {
// delay for x ms
}
}

// 3. once poll completes with a status of success update another api with the payload
await updateOtherResourcesApi(id)

// 4. once that update is complete fetch data for other api's
const result = fetchDataApi(id)

// set result of fetch
setResult(result) // redux, local state, or whatever
}
const someApiCall = async(data) => {
// 1. calls an api with a payload and that api runs an async task to update
const {id} = creationApi(data)

// 2. polls another api to see status of the async job above
let isComplete = false;
if (!isComplete) {
const status = await apiToPoll(id)
if (status === 'error') {
throw new Error('goodbye world')
} else if (status !== 'inProgress') {
isComplete = true
} else {
// delay for x ms
}
}

// 3. once poll completes with a status of success update another api with the payload
await updateOtherResourcesApi(id)

// 4. once that update is complete fetch data for other api's
const result = fetchDataApi(id)

// set result of fetch
setResult(result) // redux, local state, or whatever
}
6 Replies
rare-sapphire
rare-sapphireOP11mo ago
I'm struggling to see how this looks like with react query. I can do something like below (pardon the pseudocode) but it seems much less straightforward;
// 1. calls an api with a payload and that api runs an async task to update
const firstMutate = useMutation({
mutationFn: async (data) => {
// post to some api
return apiResponse
}
})
const firstMutateDataId = mutation.data?.id

// 2. polls another api to see status of the async job above
const resultOfJobStatus = useQuery({
queryKey: ['example'],
queryFn: () => {
// ping api and get status
},
enabled: !!firstMutateDataId
refetchInterval: (apiData) => {...} // logic for retrying based on status
})

3. once poll completes with a status of success update another api with the payload
const secondMutation = useMutation({
mutationFn: async() => {
// update a different api
}
})

const jobStatusResult = resultOfJobStatus.data
const secondMutationFn = secondMutation.mutate
useEffect(() => {
if (jobStatusResult === 'success' && firstMutateDataId) {
secondMutation(firstMutateDataId)
}
}, [jobStatusResult, firstMutateDataId])

4. once that update is complete fetch data for other api's
useQuery({
queryKey: ['anotherExample']
queryFn: () => {
// query another api
},
enabled: secondMutationFn.data === 'success' // ??
})

const callApi = (data) => {
firstMutate.mutate(data)
}
// 1. calls an api with a payload and that api runs an async task to update
const firstMutate = useMutation({
mutationFn: async (data) => {
// post to some api
return apiResponse
}
})
const firstMutateDataId = mutation.data?.id

// 2. polls another api to see status of the async job above
const resultOfJobStatus = useQuery({
queryKey: ['example'],
queryFn: () => {
// ping api and get status
},
enabled: !!firstMutateDataId
refetchInterval: (apiData) => {...} // logic for retrying based on status
})

3. once poll completes with a status of success update another api with the payload
const secondMutation = useMutation({
mutationFn: async() => {
// update a different api
}
})

const jobStatusResult = resultOfJobStatus.data
const secondMutationFn = secondMutation.mutate
useEffect(() => {
if (jobStatusResult === 'success' && firstMutateDataId) {
secondMutation(firstMutateDataId)
}
}, [jobStatusResult, firstMutateDataId])

4. once that update is complete fetch data for other api's
useQuery({
queryKey: ['anotherExample']
queryFn: () => {
// query another api
},
enabled: secondMutationFn.data === 'success' // ??
})

const callApi = (data) => {
firstMutate.mutate(data)
}
adverse-sapphire
adverse-sapphire11mo ago
Dominik has a lot of blogs on topics like this. I'd suggest a few of them, but start here: https://tkdodo.eu/blog/mastering-mutations-in-react-query
Mastering Mutations in React Query
Learn all about the concept of performing side effects on the server with React Query.
rare-sapphire
rare-sapphireOP11mo ago
Ty, I've read through that before and reading through it again. I think the part I'm struggling with the most is, steps 2 -> 3. Where I poll an api then once that api is done call a mutate on another. That's the part where code get's a little less straightforward. e.g.
const {data} = useQuery({...})
if (data.status === 'success') {
// mutate. Could do this with a useEffect or other way. But, that doesn't sit well with me.
}
const {data} = useQuery({...})
if (data.status === 'success') {
// mutate. Could do this with a useEffect or other way. But, that doesn't sit well with me.
}
I'll reread and sit on this a bit more.
adverse-sapphire
adverse-sapphire11mo ago
Have you considered WebSocket(s)? Receiving 'complete' in a socket message could replace your polling, and when complete call mutate()
adverse-sapphire
adverse-sapphire11mo ago
Using WebSockets with React Query
A step-by-step guide on how to make real-time notifications work with react-query
adverse-sapphire
adverse-sapphire11mo ago
Main thing I see with your if (data.status === 'success') without useEffect is if you're not careful that might render again and fall into the if statement more than once

Did you find this page helpful?