T
TanStack•2y ago
conventional-tan

Clearing query data without invoking a fetch

I have a resource that returns a 404 when it hasn't been previously created. A mutation is invoked, which creates the said resource - and the original query then runs and returns the object after invalidating the query. Another mutation performs a DELETE. Because I know a refetch would return a 404 - I'd prefer to prevent useQuery from refetching the data. I assumed that I could do something within the onSuccess on the mutation - but all options seem to lead to a re-fetch.
const queryKey = ['someKey']
const enabled = ref(true) // used for the original query
const isEnabled = computed(() => { return enabled.value }) // used for the original query

export const useMyComposable = () => {
const queryClient = useQueryClient()

return useMutation({
mutationFn: () => myApi.deleteConfiguration(),
onSuccess: () => {
// This keeps the original data, and causes a refetch.
// queryClient.invalidateQueries({ queryKey, })

// This clears the original data, and causes a refetch.
// queryClient.resetQueries({ queryKey, exact: true })

// This removes the entire query but doesn't cause a redraw.
// queryClient.removeQueries({ queryKey, exact: true })

// This wont work because I want to set it to undefined, but undefined
// in this case causes setQueryData to do nothing.
// queryClient.setQueryData(queryKey, () => (undefined))
},
})
})
const queryKey = ['someKey']
const enabled = ref(true) // used for the original query
const isEnabled = computed(() => { return enabled.value }) // used for the original query

export const useMyComposable = () => {
const queryClient = useQueryClient()

return useMutation({
mutationFn: () => myApi.deleteConfiguration(),
onSuccess: () => {
// This keeps the original data, and causes a refetch.
// queryClient.invalidateQueries({ queryKey, })

// This clears the original data, and causes a refetch.
// queryClient.resetQueries({ queryKey, exact: true })

// This removes the entire query but doesn't cause a redraw.
// queryClient.removeQueries({ queryKey, exact: true })

// This wont work because I want to set it to undefined, but undefined
// in this case causes setQueryData to do nothing.
// queryClient.setQueryData(queryKey, () => (undefined))
},
})
})
What am I missing? Do I need to also cancel the query after resetting perhaps?
22 Replies
extended-yellow
extended-yellow•2y ago
The query has a subscriber. What do you expect what happens if you delete the resource? Do you want to show the deleted resource or show an error?
conventional-tan
conventional-tanOP•2y ago
In the case of undefined or no data, I show a prompt to the user allowing them to create it. So really, I just want to fire the DELETE to my API, and have the cache cleared as if it had no data in it - without it re-requesting the data (since I know it would return a 404 in this case..). FWIW - there's a different mutation that ADDS the data. And in this case it works fine, because I send the POST - then invalidate the query which in turn re-requests the data (and in this case, doesn't return a 404..).
extended-yellow
extended-yellow•2y ago
Can you set the cache entry to be null?
absent-sapphire
absent-sapphire•2y ago
Yeah you can't have data be undefined in the cache, so I think you'd have to do queryClient.setQueryData(queryKey, null)
conventional-tan
conventional-tanOP•2y ago
Right, so - maybe set the initialDefault to null and set it to null. I'll give that a shot. Ok - this was the solution. I defaulted the data to null.. and set the data to null, which doesn't invoke a fresh if not stale.
conscious-sapphire
conscious-sapphire•2y ago
good that this solves it for you, I just don't fully understand the use-case. If you want to clear the cache without a refetch queryClient.removeQueries is what you want: It just removes data from the cache, nothing else. Now your comment says:
This removes the entire query but doesn't cause a redraw.
Yes, it doesn't. Because if it would re-render the component, the query would re-run, because a query that has no data starts to run the queryFn, just like if the component rendered for the first time. So it's pretty weird to remove cached data for a query that is currently rendered on screen, but don't want a refetch, but still want a re-render. That's kind of impossible 😅
conventional-tan
conventional-tanOP•2y ago
Maybe I wasn't so great at explaining this. All I really wanted was to run the mutation that deletes the object. Prior to making the request, the component was showing the data. After the delete, the component should update to reflect the lack of data. However, I didn't want useQuery to re-query the object because it was already deleted. So I wanted some way to clear that data without it re-fetching. In my rudementary test above, using removeQueries didn't cause the component to update to reflect the lack of data. setQueryData DID do this tho - once I'd updated to a null value. I think perhaps there was a mis-understanding of removing cached data, vs setting it to some form of an empty value. Your last statement is a bit confusing to me also. If you removed cache data, isn't that like an optimistic update? I.e. - you removed the data, why would the component not update to reflect that? Why can't I remove it - have the component update to reflect the lack of data, but not re-fetch?
conscious-sapphire
conscious-sapphire•2y ago
After the delete, the component should update to reflect the lack of data.
the best way to do that is probably to not render that component at all in that case. Unmount it after the delete mutation
extended-yellow
extended-yellow•2y ago
Which status should the query be in then?
conscious-sapphire
conscious-sapphire•2y ago
it's like being on <User id={1} /> and then we delete user 1 from within that page. I won't continue to render that component, but redirect the user back to the user list or somewhere else ...
conventional-tan
conventional-tanOP•2y ago
Right, but that's a pretty opinionated approach right? In my case - I'm working with a (very) old API.. which admittedly doesn't help. So I was just trying to determine what the recommended approach was. It seemed counter intuitive to me to have to completely unmount the component in this case. (the delete happens in a dialog with the parent displaying the data.. ). At the end of the day, for me - I just set the data to null now using setQueryData which works.
extended-yellow
extended-yellow•2y ago
Query is an asynchronous state manager. Can’t have state without a server. If you don’t want to use the state, don’t use the useQuery
conventional-tan
conventional-tanOP•2y ago
For context, this a a configuration object thats written via the API. It returns a 404 when you GET the object if it doesn't already exist - but when it does exist, it returns the object. There's no list involved. Not sure I said there wasn't a server.. 🙂 Its just about saving an additional API call when I know its not needed.
extended-yellow
extended-yellow•2y ago
Like Dominik said: move to a different component to not have an unnecessary useQuery.
conventional-tan
conventional-tanOP•2y ago
Hrm, there's only one useQuery, with an associated mutation. But maybe this is all just my mis-understanding of how to properly use tanstack query.
conscious-sapphire
conscious-sapphire•2y ago
it's fine to use setQueryData and set it to null. Query will be in success state with data: null; TypeScript won't like it much because types are usually tied to what the queryFn returned, but it'll work.
conventional-tan
conventional-tanOP•2y ago
Yeah, the queryFn returns a null value with the 404 result.. I knew that was going to be a problem. Are useQueries supposed to be used directly in the components? I have mine in an seperate ts module wrapped up in their own functions..
conscious-sapphire
conscious-sapphire•2y ago
Are useQueries supposed to be used directly in the components?
it's a hook, so in components or in a custom hook
conventional-tan
conventional-tanOP•2y ago
Sure.. this a composable Vue project, so its created / in the setup function. The actual useQuery is an import from elsewhere tho, and called within the component. I think thats prolly moot tho. I think what I'm doing probably makes sense for this particular API call. ok, well - thanks for the help and advice.
manual-pink
manual-pink•14mo ago
@TkDodo 🔮 Hi and sorry for writting here but it's related; currently I want to clear the entire cache after user log out. I've tried using client.clear() or client.removeQueries() but all of them refetch the active queries, is there I am missing?
eastern-cyan
eastern-cyan•14mo ago
@Emiliano Bucci the reason why that might be happening is because by the time you're calling client.clear or client.removeQueries(), you're still on the page (or even nested in the heirarchy) where query calls are being made. If you take a user to another page/screen first which doesn't deal with any query call, then your issue might be fixed.
manual-pink
manual-pink•14mo ago
@areeb Hi and thanks for the answer; yes that's why, thanks!

Did you find this page helpful?