Lorsque l'on veut afficher une boîte de confirmation dans React, on se retrouve souvent à ajouter un état open, à câbler les callbacks, puis à conditionner la suite de l'action en fonction du choix de l'utilisateur. Pour une simple suppression, cela fait beaucoup de code autour du composant qui déclenche l'action. React Call propose une autre approche : transformer un composant React en fonction que l'on pourra appeler dans la logique de notre callback directement. L'idée est particulièrement intéressante pour les boîtes de dialogue, les systèmes de notification ou les alertes
Utilisation
On commence par installer la librairie. Dans l'exemple, l'installation se fait avec Bun :
Une fois la librairie installée, on peut transformer un composant existant en composant appelable grâce à createCallable. Prenons le cas d'une boîte de confirmation. Au lieu d'avoir un composant qui reçoit un onConfirm et qui dépend d'un état externe pour savoir s'il est ouvert, on va l'exporter avec createCallable.
createCallable peut recevoir deux types TypeScript principaux :
- le type des propriétés passées au composant.
- le type de retour lorsqu'on appelera le composant.
Dans le cas d'une confirmation, la réponse est un boolean : true si l'utilisateur confirme, false s'il annule. Le composant reçoit aussi une propriété spéciale call, fournie par React Call. Elle contient notamment la méthode call.end(), qui permet de terminer l'appel et de résoudre la promesse avec une valeur.
Le composant appelable doit ensuite être placé quelque part dans l'application. Cela ne veut pas dire qu'il va s'afficher immédiatement : on définit simplement la racine dans laquelle React Call pourra injecter les instances actives.
Pour les boîtes modales ce placement n'a pas d'importance vu qu'elles utilisent déjà un portail en interne. Mais choisir le point de montage peut être intéréssant pour les éléments qui doivent ensuite être placé à un endroit particulier.
Une fois la racine en place, on peut appeler la boîte de confirmation depuis n'importe où avec ConfirmDialog.call().
On retrouve une écriture beaucoup plus directe : on demande une confirmation, on attend la réponse, puis on exécute la logique associée. Cela évite de mélanger dans le composant parent l'état d'ouverture de la modale, les callbacks et la logique métier.
Gérer les animations de fermeture
Par défaut, si le composant est retiré immédiatement du DOM lors de l'appel à call.end() mais si on veut créer un effet de disparition en CSS on peut avoir besoin d'un délai. Il est alors possible de préciser une valeur de délai lors du démontage de l'élément en second paramètre de la fonction createCallable().
Ici, le composant reste monté pendant une seconde après la fin de l'appel. Pendant ce délai, on peut utiliser call.ended pour appliquer une classe CSS ou autre.
Lancer une action asynchrone avant de fermer
Dans certains cas, dans notre boite de dialogue on veut afficher un état de chargement pour indiquer que l'action demandée est en cours. Pour cela, React Call propose un sous-package mutation-flow. On commence par rajouter une nouvelle propriétée mutationFn à notre composant.
Dans le composant, on récupère une fonction submit à l'aide du hook useMutationFlow.
submit.pending permet de désactiver le bouton pendant que l'action est en cours. Avec .orEnd(true), la boîte se ferme directement avec true si aucune fonction de mutation n'a été fournie. En revanche, si une mutationFn est passée, c'est elle qui décide quand terminer l'appel avec call.end() :
De cette manière là, le composant chargé via react-call peut connaitre l'état de l'opération original et affiché un indicateur plutôt que de se fermer directement.
call ou upsert ?
À chaque appel de ConfirmDialog.call(), React Call crée une nouvelle instance. Si on appelle plusieurs fois la méthode, plusieurs boîtes peuvent donc s'empiler les unes sur les autres. Pour les cas où l'on veut une seule instance active, on peut utiliser upsert() (la signature est la même).
Lors du premier appel upsert() se comporte comme call() et va monter votre composant. Lors d'un second appel, si le composant est toujours présent, il sera mis à jour avec les nouvelles propriétés au lieu de monter une nouvelle instance.
Le hot reload
Les composants créés avec createCallable ont besoin d'un displayName pour mieux fonctionner avec le hot reload pendant le développement.
Si vous utilisez Vite, React Call fournit un plugin qui peut se charger d'ajouter des displayName automatiquement sur vos callable.
Quand utiliser React Call ?
React Call fonctionne bien pour les composants d'interface qui retournent naturellement une valeur :
- une confirmation avant suppression.
- une alerte ou une notification.
- une boîte de dialogue qui enchaîne une action asynchrone.
- un composant que l'on veut appeler ponctuellement depuis plusieurs endroits.
Le fonctionnement reste assez simple : on prend un composant classique, on le rend appelable avec createCallable, on ajoute sa racine dans l'application, puis on l'appelle avec await. Pour ce type de besoin, l'approche est souvent plus naturelle que de multiplier les états open, setOpen et les callbacks dans les composants parents.