---
contentId: 11e74f5a-eed4-4419-a931-1c659ebd7ff3
title: Pinia Colada, les modales et l'expérience utilisateur
description: Loin d'être évident mais essentiel pour offrir une bonne expérience utilisateur, vous comprendrez quand attendre les requêtes invalidées dans Pinia Colada.
date: 2025-06-07
---

Depuis plusieurs mois, j'utilise [**Pinia Colada**](https://pinia-colada.esm.dev/). Je l'ai découvert en recherchant une solution élégante pour récupérer des données sur mon site personnel (celui que vous lisez actuellement). Depuis, je l'ai utilisé dans tous mes projets qui nécessitent des données d'une API. C'est facile à utiliser, ça fonctionne parfaitement et cela me permet de créer de meilleures expériences grâce à sa mise en cache intégrée, sa fonctionnalité de revalidation des données en arrière-plan, et la facilité de mettre en œuvre des optimistic updates.

**Pinia Colada** est une couche de récupération de données pour [**Pinia**](https://pinia.vuejs.org/), le store pour **Vue.js**, rendant la gestion d'état asynchrone d'une simplicité enfantine. Vous devriez vraiment l'essayer. Je ne peux plus commencer un nouveau projet sans lui désormais.

Cependant, j'ai récemment rencontré une difficulté en utilisant **Pinia Colada** dans une modale. Laissez-moi vous expliquer le problème et comment je l'ai résolu.

## Le Problème

Avant de me plonger dans l'explication, regardez la vidéo suivante :

<figure>
  <video autoplay loop muted playsinline>
    <source src="https://images.soubiran.dev/posts/using-pinia-colada-in-modals-without-spoiling-the-ux/the-problem.mp4" type="video/mp4">
  </video>
  <figcaption>La modale se ferme avant que les données ne soient mises à jour.</figcaption>
</figure>

Vous voyez le problème ?

Lorsque je soumets le formulaire dans la modale, le bouton "Soumettre" est désactivé et un indicateur de chargement apparaît. Jusqu'ici, tout va bien. Mais ensuite, la modale se ferme avant que les données ne soient mises à jour sur la page. L'utilisateur doit attendre, mais aucun retour ne s'affiche, ce qui nuit à l'expérience utilisateur. L'utilisateur ne sait pas si l'action a été réussie.

Vous pouvez trouver le code de cette vidéo sur <GitHubLink repo="barbapapazes/pinia-colada-await-invalidate-queries" />.

Plongeons dans les détails techniques du problème et ses causes.

Le code **Pinia Colada** que j'ai écrit pour muter les données est le suivant :

```ts
const open = ref(false)
const { mutate: createComment, isLoading: isCreatingComment } = useMutation({
  mutation: () => $fetch('/api/comments', {
    method: 'POST',
    body: comment.value
  }),
  onSettled: () => {
    queryCache.invalidateQueries({ key: ['comments'] })
  },
  onSuccess: () => {
    comment.value.text = ''
    open.value = false
  }
})
```

La variable `open` contrôle la visibilité de la modale.

L'utilisateur ouvre la modale, remplit le formulaire et clique sur le bouton "Soumettre". La mutation `createComment` est alors appelée, envoyant des données à l'API via la fonction `mutate`. En cas de succès, la fonction `onSuccess` réinitialise le formulaire et ferme la modale. Enfin, la fonction `onSettled` invalide la requête `comments` pour récupérer à nouveau les données de l'API.

La requête **Pinia Colada** ressemble à ceci :

```ts
const { state } = useQuery({
  key: ['comments'],
  query: () => $fetch('/api/comments')
})
```

Le problème est que `onSettled`, qui invalide la requête et récupère à nouveau les données, est appelé après `onSuccess`, ce qui ferme la modale. Par conséquent, l'utilisateur voit la modale se fermer avant que les données n'apparaissent sur la page, ce qui crée de la confusion. "Où est mon commentaire ? Ça a fonctionné ?"

Serait-il possible de déplacer `open.value = false` vers `onSettled`, après l'invalidation de la requête ? Malheureusement, non.

## Solutions Multiples

Pour résoudre le problème, deux solutions me sont venues à l'esprit :

- **Mises à Jour Optimistes** : C'est la meilleure solution pour l'expérience utilisateur, mettant immédiatement à jour l'interface, mais cela nécessite un code plus complexe. Ce n'est pas toujours faisable, surtout si l'API renvoie des données différentes de celles qui ont été envoyées. Par exemple, avec un système de commentaire permettant le markdown, l'API renvoie le markdown formaté, mais l'utilisateur envoie du markdown brut. Dans de tels cas, les mises à jour optimistes ne sont pas viables, et un état de chargement est la seule solution.
- **État de Chargement** : C'est la solution la plus simple. Désactiver le bouton "Soumettre", afficher un indicateur de chargement et attendre la réponse de l'API. C'est la plus commune et la plus facile à mettre en œuvre. Cependant, dans une modale, cela nécessite des ajustements par rapport à un formulaire mis en ligne dans une page.

Pour mon projet, les mises à jour optimistes n'étaient pas une option, j'ai donc dû utiliser l'état de chargement. Pourtant, lors de sa mise en œuvre, j'ai rencontré ce problème. J'ai beau avoir lu la documentation **Pinia Colada** plusieurs fois, je n'avais pas de solution. À un moment, une idée a fini par germer. J'ai sauté dans la documentation pour voir si ma compréhension pouvait être une solution viable.

## Attendre les Requêtes Invalidées

La documentation comprend une section sur ["To `await` or not to `await`"](https://pinia-colada.esm.dev/guide/query-invalidation.html#To-await-or-not-to-await) qui indique :

> In mutations, it's possible to await within the different hooks. This will effectively delay the resolution or rejection of the mutation and its asyncStatus.

C'était la clé. Je n'avais jamais compris le cas d'usage de l'attente des requêtes invalidées, mais c'est devenu clair avec ce problème. En attendant les requêtes invalidées, je pouvais retarder la fermeture de la modale jusqu'à ce que les données soient récupérées et disponibles sur la page.

Voici le code pour mettre en œuvre cette solution :

```ts
const open = ref(false)
const { mutate: createComment, isLoading: isCreatingComment } = useMutation({
  mutation: () => $fetch('/api/comments', {
    method: 'POST',
    body: comment.value
  }),
  onSettled: async (_, error) => {
    await queryCache.invalidateQueries({ key: ['comments'] })

    if (!error) {
      comment.value.text = ''
      open.value = false
    }
  },
})
```

Au lieu d'utiliser `onSuccess` pour fermer la modale, je l'ai déplacé vers `onSettled`, ajoutant un `await` avant l'invalidation du cache. Ainsi, la modale ne se ferme qu'après que les données aient été récupérées à nouveau et affichées sur la page. Simple, élégant et efficace.

<figure>
  <video autoplay loop muted playsinline>
    <source src="https://images.soubiran.dev/posts/using-pinia-colada-in-modals-without-spoiling-the-ux/the-solution.mp4" type="video/mp4">
  </video>
  <figcaption>La modale se ferme après que les données soient mises à jour.</figcaption>
</figure>

## Enfin

Ce petit problème souligne deux points importants :

- Chaque détail impacte l'expérience utilisateur. Ces petites nuances peuvent créer une grande différence à la fin.
- Lire la documentation, encore et encore, même si cela peut sembler impertinent au départ, peut aider à résoudre des problèmes comme je l'ai fait ici.

J'espère que cet article clarifie l'utilisation de **Pinia Colada** dans des modales sans dégrader l'expérience utilisateur. Si vous avez des questions ou des suggestions, n'hésitez pas à laisser un commentaire ci-dessous.

Pour voir le code en action, vous pouvez le trouver sur <GitHubLink repo="barbapapazes/pinia-colada-await-invalidate-queries" />.
