Reputation: 2723
In this code snippet, when I hover over the onSuccess
callback, it says that it's deprecated and will be removed in the next major version. The same happens with onError
and onSettled
callbacks. What do I use instead?
const { isError, error, data, refetch, isFetching } = useQuery(
["posts"],
fetchPosts,
{ enabled: false, onSuccess: () => {} }
);
Upvotes: 42
Views: 61787
Reputation: 2723
onError
You can use the onError
callback inside the queryCache
option of QueryClient
import {
QueryCache,
QueryClient,
} from "@tanstack/react-query";
import { toast } from "react-toastify";
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error) => {
toast(`Something went wrong: ${error.message}`),
}
}),
})
And then wrap your application with it like you normally would:
function App() {
return (
<QueryClientProvider client={queryClient}>
<Posts />
</QueryClientProvider>
)
}
If you need more customizable onError
side effects, then use the meta
option of the useQuery
hook. You can pass it any data you want, which you can then use to determine which side effect to trigger.
I came up with this solution:
Define an enum containing the error codes for triggering the side effects:
export const enum TErrCodes {
POSTS_FETCH_FAILED,
// other error codes...
}
Specify the error code in the meta
option of the useQuery
hook:
const query = useQuery(["posts"], fetchPosts, {
meta: { errCode: TErrCodes.POSTS_FETCH_FAILED },
});
Detect the error code in the queryCache
onError
callback and perform the side effect you need. I'm showing notifications using the react-toastify
library as an example:
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: queryCacheOnError,
}),
});
function queryCacheOnError(err: unknown, query: Query) {
switch (query.meta?.errCode) {
case TErrCodes.POSTS_FETCH_FAILED:
return toast.error("Could not fetch posts");
default:
return toast.error("Something went wrong");
}
}
Alternatively, you can trigger onError
side effects from the query function, like so:
async function fetchPosts() {
try {
const resp = await axios.get<TPost[]>("/posts");
return resp.data;
} catch (err) {
if (!isKnownErr(err)) {
return toast("Something went wrong");
}
switch (err.response.data.errCode) {
case "INVALID_CREDENTIALS":
toast("Invalid sign-in credentials");
break;
// more cases...
default:
toast("Something went wrong");
}
throw new Error(); // prevent query from being successful
}
}
const query = useQuery(["posts"], fetchPosts);
onSuccess
You can trigger the onSuccess
side-effects from inside the query function as well, like this:
async function fetchPosts() {
const resp = await axios.get<TPost[]>("/postss");
yourSideEffect();
return resp.data;
}
const query = useQuery(["posts"], fetchPosts);
This is mostly taken from this article
The callbacks have inconsistent behavior. They can be called multiple times for the same query, leading to duplication of side effects or state updates. For example, this can lead to showing 2 duplicate error notifications for the same query.
The callbacks have been misused by some developers to perform state syncing operations, which often leads to really hard-to-spot bugs. You should instead derive state by using something like const postsCount = posts.length;
. The postsCount
variable will be updated whenever the component re-renders.
Using callbacks sometimes leads to unexpected behavior with redux and other state managers.
Here's another article on the topic
Hope you found this helpful!
Upvotes: 62