T
TanStack3y ago
harsh-harlequin

Custom hook to handle multiple queries/mutation combined by domain e.g. useCart

So I am refactoring a code in a project and I decided to dive with React Query. One Approach I tried to use is to combine all of the queries into a single hook useCart. I thought that this would be a good idea as it will basically help me have a shared isLoading state amongst other things for the cart itself and all mutations related to it. I still dont know if its a good idea to approach it like this, but the problem that I have encountered is that isLoading isnt really working.
export const useCartRq = () => {
const { locale } = useLocale()

const { data: cartData, isLoading: isCartLoading } = getCartQuery({ locale })

const {
mutate: addItemInCart,
mutateAsync: addItemInCartAsync,
isLoading: isCartAddingItemLoading,
} = addItemInCartMutation()

const {
mutate: updateItemInCart,
mutateAsync: updateItemInCartAsync,
isLoading: isCartUpdatingItemLoading,
} = updateItemInCartMutation()

const {
mutate: deleteCart,
mutateAsync: deleteCartAsync,
isLoading: isCartDeleteLoading,
} = deleteCartMutation()

const {
mutate: convertCartCurrency,
mutateAsync: convertCartCurrencyAsync,
isLoading: isCartConvertingLoading,
} = convertCartCurrencyMutation()

const isLoading =
isCartLoading ||
isCartAddingItemLoading ||
isCartUpdatingItemLoading ||
isCartDeleteLoading ||
isCartConvertingLoading

return {
cart: cartData,
isLoading,
addItemInCart,
addItemInCartAsync,
updateItemInCart,
updateItemInCartAsync,
deleteCart,
deleteCartAsync,
convertCartCurrency,
convertCartCurrencyAsync,
}
}
export const useCartRq = () => {
const { locale } = useLocale()

const { data: cartData, isLoading: isCartLoading } = getCartQuery({ locale })

const {
mutate: addItemInCart,
mutateAsync: addItemInCartAsync,
isLoading: isCartAddingItemLoading,
} = addItemInCartMutation()

const {
mutate: updateItemInCart,
mutateAsync: updateItemInCartAsync,
isLoading: isCartUpdatingItemLoading,
} = updateItemInCartMutation()

const {
mutate: deleteCart,
mutateAsync: deleteCartAsync,
isLoading: isCartDeleteLoading,
} = deleteCartMutation()

const {
mutate: convertCartCurrency,
mutateAsync: convertCartCurrencyAsync,
isLoading: isCartConvertingLoading,
} = convertCartCurrencyMutation()

const isLoading =
isCartLoading ||
isCartAddingItemLoading ||
isCartUpdatingItemLoading ||
isCartDeleteLoading ||
isCartConvertingLoading

return {
cart: cartData,
isLoading,
addItemInCart,
addItemInCartAsync,
updateItemInCart,
updateItemInCartAsync,
deleteCart,
deleteCartAsync,
convertCartCurrency,
convertCartCurrencyAsync,
}
}
And then I would use this as:
// Called in one component
const { updateItemInCart, isLoading } = useCartRq()

...updateItemInCart(...)...
console.log(isLoading); // works as expected

// Called in another component
const { isLoading } = useCartRq();
console.log(isLoading); // doesn't work
// Called in one component
const { updateItemInCart, isLoading } = useCartRq()

...updateItemInCart(...)...
console.log(isLoading); // works as expected

// Called in another component
const { isLoading } = useCartRq();
console.log(isLoading); // doesn't work
However if I log isCartUpdatingItemLoading within the useCartRq hook I can see that it changes to true for a brief moment, and its status is also changing as follows idle -> loading -> idle -> success If I use the updateItemInCartMutation as a standalone hook, then obviously isLoading works as expected. What am I missing/misunderstanding about RQ that will allow me to create a more "generalized" hook to handle "domain logic"? P.S. What I found out is that isLoading does work if I log it in the component that is actually using updateItemInCart, however I am trying to use the isLoading in multiple other components
No description
4 Replies
harsh-harlequin
harsh-harlequinOP3y ago
bump
unwilling-turquoise
unwilling-turquoise3y ago
As far as I understand, mutations states are not "global" like queries since they don't have a key, so if you call useCartRq in 2 different components and you trigger a mutation in one component, the other component should still have isLoading = false
harsh-harlequin
harsh-harlequinOP3y ago
Yes, precisely, so what strategy can be used to conveniently address the issue, adding a global context just to add an "isLoading" doesnt seem very elegant and kinda goes in reverse of what we are trying to do with ReactQuery in general can I artificially set the isLoading state to true/false of a query through the query client? Given that the mutations are indeed relevant to an existing query so for example when I am adding a product and the mutationFn for that is executing, in it something like queryClient.setIsLoading(["cart"], true)
unwilling-turquoise
unwilling-turquoise3y ago
If you add keys to your mutations, you should be able to use useIsMutating and filter by key: https://tanstack.com/query/latest/docs/react/reference/useIsMutating
useIsMutating | TanStack Query Docs
useIsMutating is an optional hook that returns the number of mutations that your application is fetching (useful for app-wide loading indicators). `tsx

Did you find this page helpful?