arbret/frontend/app/hooks/useMutation.ts

60 lines
1.6 KiB
TypeScript
Raw Normal View History

import { useState, useCallback } from "react";
/**
* Hook for handling mutations (create, update, delete operations).
* Manages loading state and error handling for async mutations.
*
* @param mutationFn - Function that performs the mutation and returns a Promise
* @param options - Configuration options
* @returns Object containing mutate function, loading state, and error
*/
export function useMutation<TArgs, TResponse = void>(
mutationFn: (args: TArgs) => Promise<TResponse>,
options: {
/** Callback called on successful mutation */
onSuccess?: (data: TResponse) => void;
/** Callback called on mutation error */
onError?: (err: unknown) => void;
} = {}
): {
mutate: (args: TArgs) => Promise<TResponse | undefined>;
isLoading: boolean;
error: string | null;
} {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const mutate = useCallback(
async (args: TArgs): Promise<TResponse | undefined> => {
setIsLoading(true);
setError(null);
try {
const result = await mutationFn(args);
if (options.onSuccess) {
options.onSuccess(result);
}
return result;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "Operation failed";
setError(errorMessage);
if (options.onError) {
options.onError(err);
} else {
console.error("Mutation failed:", err);
}
throw err;
} finally {
setIsLoading(false);
}
},
[mutationFn, options]
);
return {
mutate,
isLoading,
error,
};
}