TanStack Query is a powerful tool for managing server state in React applications. However, configuring queries and mutations repeatedly in a project can lead to code duplication and inconsistency. In this tutorial, weβll centralize the configuration of queries and mutations using two custom hooks: useApiGet and useApiSend. π
These hooks will:
- Provide consistent retry logic.
- Handle success and error scenarios.
- Allow query invalidation post-mutation.
- Minimize boilerplate across your project.
Why Centralize Query and Mutation Configurations?
Centralizing these configurations ensures:
Reusability: Use the same logic for all queries and mutations.
Consistency: Avoid discrepancies in retry mechanisms or error handling.
Maintainability: Update logic in one place and propagate it across your application.
The Code
Hereβs the implementation of the useApiGet
and useApiSend
hooks:
1. Custom Query Hook: useApiGet
This hook simplifies useQuery
with retry logic and default options.
import { isAxiosError } from 'axios';
import { useQuery, QueryKey, QueryFunction } from '@tanstack/react-query';
const HTTP_STATUS_TO_NOT_RETRY = [400, 401, 403, 404, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511];
const MAX_RETRY = 3;
type ApiGetProps = {
key: QueryKey;
fn: Promise<any> | QueryFunction<unknown, QueryKey, never> | undefined;
options?: any;
};
const useApiGet = <T>({ key, fn, options }: ApiGetProps) =>
useQuery<T>({
queryKey: key,
queryFn: fn,
refetchOnWindowFocus: false,
retry: (failureCount, error: any) => {
if (failureCount >= MAX_RETRY) return false;
if (isAxiosError(error) && HTTP_STATUS_TO_NOT_RETRY.includes(error.response?.status ?? 0)) return false;
return true;
},
...options,
});
Key Features:
-
Retry Mechanism: Retries up to
MAX_RETRY
times for network issues. - Avoids Retry on Certain Status Codes: Stops retrying for non-network errors like 404 or 500.
- Customizable Options: Allows overriding default behaviors via the options prop.
2. Custom Mutation Hook: useApiSend
This hook builds on useMutation
and includes features like query invalidation and callbacks for success/error handling.
import { useMutation, useQueryClient, InvalidateQueryFilters } from '@tanstack/react-query';
import { isAxiosError } from 'axios';
type ApiSendProps<T> = {
fn: ((data: T) => Promise<any>) | (() => Promise<any>);
success?: (data: any) => void;
error?: (error: any) => void;
invalidateKey?: InvalidateQueryFilters[] | undefined;
options?: any;
};
const useApiSend = <T>({ fn, success, error, invalidateKey, options }: ApiSendProps<T>) => {
const queryClient = useQueryClient();
return useMutation<unknown, Error, T>({
mutationFn: fn,
onSuccess: (data) => {
if (invalidateKey) {
invalidateKey.forEach((key) => queryClient.invalidateQueries(key));
}
if (success) success(data);
},
onError: error,
retry: (failureCount, error: any) => {
if (failureCount > 2) return false;
if (isAxiosError(error) && HTTP_STATUS_TO_NOT_RETRY.includes(error.response?.status ?? 0)) return false;
return true;
},
...options,
});
};
Key Features:
- Query Invalidation: Automatically refreshes specific queries when a mutation succeeds.
- Custom Callbacks: Executes success or error callbacks for further handling.
- Retry Logic: Similar to useApiGet, retries only on network-related errors.
How to Use These Hooks
Fetching Data with useApiGet
Use this hook to fetch data consistently across your application.
import { useApiGet } from './apiHooks';
const UserProfile = () => {
const { data, isLoading, error } = useApiGet({
key: ['user', 'profile'],
fn: axios.get('/api/profile'),
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error loading profile</p>;
return <div>{data?.name}</div>;
};
Sending Data with useApiSend
Use this hook to send data while managing invalidation and success/error callbacks.
import { useApiSend } from './apiHooks';
const UpdateProfile = () => {
const { mutate, isLoading } = useApiSend({
fn: (data) => axios.post('/api/profile', data),
success: () => alert('Profile updated successfully!'),
error: (err) => console.error('Failed to update profile:', err),
invalidateKey: [['user', 'profile']],
});
const handleSubmit = () => {
mutate({ name: 'New Name' });
};
return (
<button onClick={handleSubmit} disabled={isLoading}>
Update Profile
</button>
);
};
Conclusion
Centralizing the configuration of queries and mutations using TanStack Query is a game-changer for managing server state in React applications. With useApiGet and useApiSend, you can streamline logic, improve consistency, and reduce code duplication.
Start implementing these hooks today, and let TanStack Query simplify your state management journey!. Happy coding! πβ¨
Top comments (0)