Pinia Colada, les modales et l'expérience utilisateur

Depuis plusieurs mois, j'utilise Pinia Colada. 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, 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 :

La modale se ferme avant que les données ne soient mises à jour.

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 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" 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.

La modale se ferme après que les données soient mises à jour.

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 pinia-colada-await-invalidate-queries.

Pd

Merci de me lire ! Je m'appelle Estéban, et j'adore écrire sur le développement web.

Je code depuis plusieurs années maintenant, et j'apprends encore de nouvelles choses chaque jour. J'aime partager mes connaissances avec les autres, car j'aurais aimé avoir accès à des ressources aussi claires et complètes lorsque j'ai commencé à apprendre la programmation.

Si vous avez des questions ou souhaitez discuter, n'hésitez pas à commenter ci-dessous ou à me contacter sur Bluesky, X, et LinkedIn.

J'espère que vous avez apprécié cet article et appris quelque chose de nouveau. N'hésitez pas à le partager avec vos amis ou sur les réseaux sociaux, et laissez un commentaire ou une réaction ci-dessous—cela me ferait très plaisir ! Si vous souhaitez soutenir mon travail, vous pouvez me sponsoriser sur GitHub !

Réactions

Discussions

Ajouter un commentaire

Vous devez être connecté pour accéder à cette fonctionnalité.

Soutenez mon travail
Suivez-moi sur