import { useRouter } from "@tanstack/react-router";
import { useState } from "react";
type ActionState = { status: "idle" } | { status: "pending" } | { status: "error"; message: string };
function messageOf(error: unknown): string {
if (error instanceof Error && error.message) return error.message;
return "Something went wrong";
}
/**
* Runs a server-function mutation, tracks its pending/error state and refreshes route loaders on success.
*/
export function useAction<TInput, TResult>(
action: (input: TInput) => Promise<TResult>,
options: { invalidate?: boolean } = {},
) {
const router = useRouter();
const [state, setState] = useState<ActionState>({ status: "idle" });
const invalidate = options.invalidate ?? true;
async function run(input: TInput): Promise<TResult | undefined> {
setState({ status: "pending" });
try {
const result = await action(input);
if (invalidate) await router.invalidate();
setState({ status: "idle" });
return result;
} catch (error) {
setState({ status: "error", message: messageOf(error) });
return undefined;
}
}
return {
run,
pending: state.status === "pending",
error: state.status === "error" ? state.message : null,
reset: () => setState({ status: "idle" }),
};