Reputation: 800
I'm trying to invalidate queries every times users press button "likes" to refresh all queries but without success so far despite following the docs.
I have a component that get data :
const {
data: resultsEnCours,
isLoading,
isError,
} = useQueryGetEvents("homeencours", { currentEvents: true });
This is a custom hook which look like this :
const useQueryGetEvents = (nameQuery, params, callback) => {
const [refetch, setRefetch] = React.useState(null);
const refetchData = () => setRefetch(Date.now()); // => manual refresh
const { user } = React.useContext(AuthContext);
const { location } = React.useContext(SearchContext);
const { isLoading, isError, data } = useQuery(
[nameQuery, refetch],
() => getFromApi(user.token, "getEvents", { id_user: user.infoUser.id, ...params }),
// home params => "started", "upcoming", "participants"
{
select: React.useCallback(({ data }) => filterAndSortByResults(data, location, callback), []),
}
);
return { data, isLoading, isError, refetchData };
};
export default useQueryGetEvents;
And I have another component "ButtonLikeEvent" which allow the user to like or unlike an event:
import { useMutation, useQueryClient } from "react-query";
import { postFromApi } from "../../api/routes";
...other imports
const ButtonLikeEvent = ({ item, color = "#fb3958" }) => {
const queryClient = useQueryClient();
const {
user: {
token,
infoUser: { id },
},
} = React.useContext(AuthContext);
const [isFavorite, setIsFavorite] = React.useState(item.isFavorite);
const likeEventMutation = useMutation((object) => postFromApi(token, "postLikeEvent", object));
const dislikeEventMutation = useMutation((object) =>
postFromApi(token, "postDislikeEvent", object)
);
const callApi = () => {
if (!isFavorite) {
likeEventMutation.mutate(
{ id_events: item.id, id_user: id },
{
onSuccess() {
queryClient.invalidateQueries();
console.log("liked");
},
}
);
} else {
dislikeEventMutation.mutate(
{ id_events: item.id, id_user: id },
{
onSuccess() {
queryClient.invalidateQueries();
console.log("disliked");
},
}
);
}
};
return (
<Ionicons
onPress={() => {
setIsFavorite((prev) => !prev);
callApi();
}}
name={isFavorite ? "heart" : "heart-outline"}
size={30}
color={color} //
/>
);
};
export default ButtonLikeEvent;
Every time an user click on that button I'd like to invalidate queries (because many screens shows the like button).
The console.log is displayed on success but the queries are not invalidate.
Any idea ?
thanks
Upvotes: 43
Views: 77738
Reputation: 6842
When using ensureQueryData
to retrieve data from cache, one interesting challenge surfaced: the invalidateQueries
API method did not work for data invalidation 😱😱😱. I wanted to retrieve data from cache if exist or fetching otherwise.
Thankfully, React Query
has a different API
for this particular situation:
queryClient.removeQueries({ queryKey: ["queryKey"] });
Upvotes: 2
Reputation: 1074
In my case I supplied a number
type as a query key in useQuery
and string
in invalidateQueries
, thus not invalidating the query.
Upvotes: 3
Reputation: 1
My problem was that i had two QueryClientProvider one in layout ans second in other layout
Upvotes: 0
Reputation: 1178
For me nothing worked, so i just detect a route change in the parent component and refetch when the route changes:
import { useLocation } from 'react-router-dom';
...
const location = useLocation();
const { data: tagNames, refetch } = useGetTagNames();
...
useEffect(() => {
refetch();
}, [location]);
Upvotes: 1
Reputation: 1
Same issue. Success and error at the same time. The problem was in that line:
const queryClient = useQueryClient
I've just forget brackets in the end.
const queryClient = useQueryClient()
Upvotes: -1
Reputation: 252
As suggested by GamsBasallo and N.T. Dang on MasterPiece's answer, if you're using ReactRouter, on the action
function you have to add the refetchType
to all
and await the invalidating function. That is because queries are in inactive
state inside ReactRouter action
function.
Helpful doc :
https://tanstack.com/query/latest/docs/reference/QueryClient/#queryclientinvalidatequeries
https://tkdodo.eu/blog/react-query-meets-react-router#invalidating-in-actions
Upvotes: 0
Reputation: 23
WHEN USING SSR IMPLEMENTATION WITH NEXTJS 13
When following the implementation enforced on TanStack's docs, you would be encouraged to create a request based client for Server Side and a singleton client for Client Side.
But when using queryClient.invalidateQueries
, be sure to use queryClient from useQueryClient
instead of importing your global client.
Here is an example of how to do it when invalidating in a mutation.
export const useAddProductToCartMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (data: { productId: number }) => {
return await addProductToCart(data.productId);
},
onSettled: async () => {
queryClient.invalidateQueries({
queryKey: ["cart"],
refetchType: "all",
});
},
});
};
Upvotes: 0
Reputation: 31
It seems like I have been dropped on my head in my childhood because I made a custom hook and spread the config props in the wrong way.
Do this:
const { onSuccess, ...restConfig } = config || {};
return useMutation({
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({
queryKey: [productsQueryKeys.PRODUCTS],
exact: true,
});
onSuccess && onSuccess(data, variables, context);
},
...restConfig, <---- make sure you exclude the fields you're using
mutationFn: createProduct,
});
Instead of this:
const { onSuccess } = config || {};
return useMutation({
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({
queryKey: [productsQueryKeys.PRODUCTS],
exact: true,
});
onSuccess && onSuccess(data, variables, context);
},
...config, <---- who raised you like this?
mutationFn: createProduct,
});
Upvotes: 0
Reputation: 21384
UPDATE: My issue was I incorrectly set up React Query as follows:
export default function App() {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
...
</QueryClientProvider>
);
}
Fix:
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
...
</QueryClientProvider>
);
}
Subtle, but changes everything.
My issue is that I have react-router-dom
's useNavigate()
.navigate()
, i.e.:
const router = useRouter();
router.push(`${path}?field=${name}`);
which either clears the cache or remounts components, but the important effect is the cache is cleared (can be observed through React Query Devtools).
Since the cache is cleared but the components stay, doing invalidateQueries()
has no effect, as there's nothing on the cache.
It is the exact issue described here: https://www.reddit.com/r/learnreactjs/comments/uqd9sl/reactquery_cache_being_cleared_when_navigating/
I don't know how to solve this, so my "workaround" is to avoid using navigate()
when possible. Although this is not ideal and I think I will get this issue again later. :'(
Upvotes: 6
Reputation: 564
In my case, invalidate queries didn't work because I tried to invalidate query that was used in another page (the useQuery that used this case wasn't rendered).
I had 2 options to solve this:
refetchInactive
option like this: queryClient.invalidateQueries(key, { refetchInactive: true })
, which will refetch the data instead of "stale" it.refetchOnMount: true
option when calling useQuery
, which will refetch the data on mount regardless if it is stale.Upvotes: 9
Reputation: 5214
I accidently used
const queryClient = new QueryClient();
instead of
const queryClient = useQueryClient();
new QueryClient()
creates a new react-query Client(which is usually passed to a context wrapped over your App), this should be done only once.
wheres useQueryClient
returns a reference to the value that the react-query Client context wrapped around your app provides. You should use it inside your components.
Upvotes: 57
Reputation: 151
I had the same issue and in my case the problem was that I imported the wrong client.
I used vscode's autocomplete and accidentally imported it from wagmi
instead of @tanstack/react-query
.
Upvotes: 2
Reputation: 1633
My issue was that I had:
const { isLoading, data } = useQuery(
CACHE_KEY,
async () => apiFunction(foo)
);
When in fact I needed:
const { isLoading, data } = useQuery(
CACHE_KEY,
async () => await apiFunction(foo)
);
Very subtle and easy to miss!
Upvotes: 0
Reputation: 121
I had the same problem where everything was working correctly locally, but not in production. The issue was due to the cache of my Nginx server preventing data updates. To fix this, I had to add the directive
add_header 'Cache-Control' 'no-cache' always;
to my Nginx server configuration.
Upvotes: 0
Reputation: 2656
In my case the invalidateQueries did not work after the app was hot-reloaded (after a change).
Very weird behaviour, but it takes a while before you figured out the hot-reloading is the culprit.
Upvotes: 1
Reputation: 148
An additional reason for why your queries might not be properly invalidating is if you aren't returning the promise-based function in mutationFn
.
In my case, I made the mistake of calling the promise-based function without returning it. For example:
function useUpdateObject(id) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (newObj) => {
// MISTAKE! Need to return ky.put(...)
ky.put(API_ROUTE, { json: newObj }).json()
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["object", id],
});
},
});
}
Upvotes: 0
Reputation: 785
Also, there is one more reason that was not mentioned before:
If you used enabled
option in useQuery
than such queries are ignored by invalidateQueries
. From docs:
https://tanstack.com/query/latest/docs/react/guides/disabling-queries
The query will ignore query client invalidateQueries and refetchQueries calls that would normally result in the query refetching.
Upvotes: 61
Reputation: 165
In my case I was trying to use the queryClient
in a component with a higher level than the QueryClientProvider
in the components tree. You have to use the useQueryClient
hook in a component wrapped by the QueryClientProvider
.
<MyFirstComponent> <!-- I was using it in My FirstComponent. It is outside the QueryClient Context so it is not going to work there -->
<QueryClientProvider>
<MySecondComponent /> <!-- You can use the queryClient here -->
</QueryClientProvider>
</MyFirstComponent>
Upvotes: 2
Reputation: 493
In my case, all I had to do was await the promise that invalidateQueries
returns before navigating away (using react-router).
Upvotes: 11
Reputation: 171
Both Samil and Eliav ideas combined worked for me:
const queryClient = new QueryClient();
const queryClient = useQueryClient();
Upvotes: 7
Reputation: 462
QueryClient should be instantiated on the top of your app. (app.js or index.js)
Sample
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
document.getElementById('root'));
Upvotes: 2
Reputation: 28733
The two most common issues why query invalidation is not working are:
keys are not matching, so you are trying to invalidate something that doesn't exist in the cache. Inspecting the devtools helps here. Oftentimes, it's an issue with number/string (for example, if an id comes from the url, where it is a string, but in your key, it's a number). You can also try manually clicking the invalidate
button in the devtools, so you will see that invalidation per-se works.
the queryClient
is not stable across re-renders. This happens if you call new QueryClient()
inside the render method of a component that then re-renders. Make sure that the queryClient is stable.
Upvotes: 22