improvement: loading state added to buttons who interact with backend.

This commit is contained in:
2026-07-17 10:58:04 +03:30
parent 68fc9d5d6d
commit d2ad1fd7b6
23 changed files with 240 additions and 144 deletions

View File

@@ -0,0 +1,50 @@
'use client';
import { useCallback, useRef, useState } from 'react';
/**
* Runs an async action at most once at a time (sync re-entry guard via ref).
* Use for API mutations from buttons/controls that are not the shared Button,
* or when several controls share one pending flag.
*/
export function useAsyncAction() {
const [pending, setPending] = useState(false);
const pendingRef = useRef(false);
const run = useCallback(async <T,>(fn: () => Promise<T>): Promise<T | undefined> => {
if (pendingRef.current) return undefined;
pendingRef.current = true;
setPending(true);
try {
return await fn();
} finally {
pendingRef.current = false;
setPending(false);
}
}, []);
return { pending, run };
}
/**
* Same as useAsyncAction, but tracks which row/id is in flight so list UIs
* can disable sibling actions while one mutation runs.
*/
export function useAsyncActionById() {
const [pendingId, setPendingId] = useState<string | null>(null);
const pendingIdRef = useRef<string | null>(null);
const run = useCallback(async <T,>(id: string, fn: () => Promise<T>): Promise<T | undefined> => {
if (pendingIdRef.current) return undefined;
pendingIdRef.current = id;
setPendingId(id);
try {
return await fn();
} finally {
pendingIdRef.current = null;
setPendingId(null);
}
}, []);
return { pendingId, run };
}