T
TanStack•2y ago
quickest-silver

How do I keep derived data in sync

Hi All! Like most here I'm pretty new to react query and I don't know I'm complicating things too much. So I'm trying to use react query as a state manager but I'm having an issue trying to keep derived data in sync. My setup is like this, I have multiple api hooks, each with useQuery and useMutations, I then have another hook to consolidate/combine the hooks and create some derived data. I have a component with a button that will run one or more of the mutations from the combined hooks. I want to use the cached data as the derived data so that I don't have to use the useEffects and useState and what not but I don't know how to go about it. I hope this makes sense. Thank you for your time 🙂
const useApi1 = () => {
const queryClient = useQueryClient()

return {
get: useQuery({
queryKey: ['api1'],
queryFn: async () => await axiosPromise1.get(),
}),
put: useMutation({
mutationFn: axiosPromise1.put,
onSuccess: async () =>
await queryClient.invalidateQueries({ queryKey: ['api1'] }),
}),
getState: () => queryClient.getQueryState(['api1']),
}
}

const useCombinedApis = () => {
const api1 = useApi1()
const api2 = useApi2() // similar to api1

const derivedCombined = combinesValues(api1.get.data, api2.get.data) // the data I want to keep in sync

return { api1, api2, derivedCombined }
}

export const MyReactComp = () => {
const { derivedCombined } = useCombinedApis()

const mainFunction = async (data) => {
await api1.put.mutateAsync(data) // this func takes 300ms
const updatedDataFromApi1 = api1.getState() // this const is up to date

await functionThatNeedsTheDerivedData(derivedCombined) // this will fail because derived data is not in sync
}

return (
<>
<button onClick={() => mainFunction('my data')}>click me</button>
some stuff here
</>
)
}
const useApi1 = () => {
const queryClient = useQueryClient()

return {
get: useQuery({
queryKey: ['api1'],
queryFn: async () => await axiosPromise1.get(),
}),
put: useMutation({
mutationFn: axiosPromise1.put,
onSuccess: async () =>
await queryClient.invalidateQueries({ queryKey: ['api1'] }),
}),
getState: () => queryClient.getQueryState(['api1']),
}
}

const useCombinedApis = () => {
const api1 = useApi1()
const api2 = useApi2() // similar to api1

const derivedCombined = combinesValues(api1.get.data, api2.get.data) // the data I want to keep in sync

return { api1, api2, derivedCombined }
}

export const MyReactComp = () => {
const { derivedCombined } = useCombinedApis()

const mainFunction = async (data) => {
await api1.put.mutateAsync(data) // this func takes 300ms
const updatedDataFromApi1 = api1.getState() // this const is up to date

await functionThatNeedsTheDerivedData(derivedCombined) // this will fail because derived data is not in sync
}

return (
<>
<button onClick={() => mainFunction('my data')}>click me</button>
some stuff here
</>
)
}
3 Replies
broad-brown
broad-brown•2y ago
You cannot expect a value from a closure to update inside an event handler because that's not how react works
quickest-silver
quickest-silverOP•2y ago
@TkDodo 🔮, thank you so much for the response. I knew I was overlooking something and the answer was closures If y'all have suggestions on how create derived data that would be great. My naive approach is probably going to involve some useEffect at least, I think Thanks
absent-sapphire
absent-sapphire•2y ago
Would be interested in any further suggestions/feedback here. I have a similar situation where I am attempting to combine data from two different sources using Dependent queries and Query Invalidation. However, I haven't had much success. I don't have any mutations in my case, I am simply getting data from two different sources and deriving data from both of them. One hook only gets data, the other one calls the prior hook, gets data and computes derived values. The enabled part seemed to work in order enforce the required dependency, but the invalidation never succeeded (the query remains stale).

Did you find this page helpful?