npm i @tanstack/react-query
npm i -D @tanstack/eslint-plugin-query// App.jsx
// Create a client
const queryClient = new QueryClient()
function App() {
return (
// Provide the client to your App
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}import { useQuery } from "@tanstack/react-query";
import { fetchQuestions } from "@/lib/api";
const {
data: questions,
isLoading,
isError,
} = useQuery({
queryKey: ["questions"],
queryFn: () => fetchQuestions(),
staleTime: 1000 * 60 * 60 * 24, // 1 day
});// hooks/useMutation.tsx
import { useMutation, useQueryClient } from "@tanstack/react-query";
/**
* Generic mutation hook that tracks loading state by ID and invalidates queries
* @param mutationFn - The mutation function to execute
* @param invalidateQueryKeys - Query keys to invalidate on success
* @returns [isMutating, mutate] - Function to check if item is mutating and mutate function
*/
function useMutationWrapper<TVariables extends { id: string }, TData = unknown>(
mutationFn: (variables: TVariables) => Promise<TData>,
invalidateQueryKeys: string[] = []
) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn,
onSuccess: () => {
invalidateQueryKeys.forEach((key) => {
queryClient.invalidateQueries({ queryKey: [key] });
});
},
});
/**
* Check if a specific item is currently being mutated `mutation.variables?.id === itemId;`
*/
const isMutating = (itemId: string): boolean => {
return mutation.isPending && mutation.variables?.id === itemId;
};
return [isMutating, mutation.mutate] as const;
}
export default useMutationWrapper;Usage Initialize the hook, returns the loading state and mutationFn Takes two params - fetchFn, invalidationKeyArray
const [isStarredMutating, mutateStarred] = useMutationWrapper<
{ id: string; starred: boolean },
QuestionDTO
>(({ id, starred }) => toggleQuestionStarredAPI(id, starred), ["questions"]);
const onToggleStarredBtnClick = (questionId: string, currentValue: boolean) => {
mutateStarred({ id: questionId, starred: !currentValue });
};