Implementation de la notion d'espace d'investissement

This commit is contained in:
ocroguennec committed 2026-09-17 18:25:42 +02:00
1 parent 6b6cad7cd6
commit 4b7df66862
54 files changed
+6782 -288

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
import { useCallback, useRef, useState } from 'react';
/**
* useAsyncAction — garde anti-double-exécution pour un handler asynchrone
* (soumission de formulaire, suppression, action d'un bouton…).
*
* Contexte : sur une connexion lente vers le serveur (VPN, 4G…), rien
* n'indiquait visuellement qu'une requête était en cours. L'utilisateur,
* ne voyant pas de retour, cliquait une seconde fois sur "Créer"/"Enregistrer"
* avant la fermeture de la modale (qui n'intervient qu'après le succès de la
* requête) — ce qui déclenchait un second POST et doublait la saisie en base.
*
* `run` protège contre ce phénomène à deux niveaux :
* 1. Une ref (donc synchrone, pas soumise au re-render React) bloque tout
* appel concurrent tant qu'un premier est en cours — même un double-clic
* très rapproché, avant que le bouton n'ait eu le temps de se désactiver
* visuellement, est ignoré.
* 2. `pending` (état React) permet en plus de désactiver l'UI (bouton,
* fermeture de la modale) et d'afficher un indicateur de chargement,
* pour que l'utilisateur sache que sa requête est bien partie.
*
* Usage :
* const { run, pending } = useAsyncAction();
* ...
* <button disabled={pending} onClick={() => run(submit)}>
* {pending ? 'Enregistrement…' : 'Enregistrer'}
* </button>
*
* `run` transmet ses arguments au handler (utile pour un onSubmit de <form>
* qui reçoit l'événement) et retourne sa valeur de résolution. Un appel
* ignoré (déjà en cours) résout silencieusement sur `undefined`.
*/
export function useAsyncAction() {
const runningRef = useRef(false);
const [pending, setPending] = useState(false);
const run = useCallback(async (handler, ...args) => {
if (runningRef.current) return undefined;
runningRef.current = true;
setPending(true);
try {
return await handler(...args);
} finally {
runningRef.current = false;
setPending(false);
}
}, []);
return { run, pending };
}