import { useRouter } from "@tanstack/react-router";
import { useOptimistic, useState, useTransition } from "react";
function messageOf(error: unknown): string {
return error instanceof Error && error.message ? error.message : "Something went wrong";
}
/**
* Applies a mutation's expected result immediately and keeps it until the server call and the
* loader refresh both finish, so toggles like star/follow/watch never flash the stale value.
* On failure the optimistic value is dropped automatically and the error is exposed.
*/
export function useOptimisticMutation<TState, TInput>(
serverState: TState,
reduce: (state: TState, input: TInput) => TState,
action: (input: TInput) => Promise<unknown>,
) {
const router = useRouter();
const [state, apply] = useOptimistic(serverState, reduce);
const [pending, startTransition] = useTransition();
const [error, setError] = useState<string | null>(null);
function run(input: TInput) {
setError(null);
startTransition(async () => {
apply(input);
try {
await action(input);
await router.invalidate();
} catch (caught) {
setError(messageOf(caught));
}
});
}
return { state, run, pending, error };
}