Gestion d'état Vue 3 pour panneaux admin : Pinia vs local
Gestion d'état Vue 3 pour panneaux admin : choisissez entre Pinia, provide/inject et l'état local à l'aide d'exemples réels comme filtres, brouillons et onglets.

Ce qui rend l'état compliqué dans les panneaux admin
Les panneaux d'administration donnent l'impression d'être lourds en état parce qu'ils rassemblent beaucoup d'éléments sur un seul écran. Un tableau n'est pas que des données : il y a aussi le tri, les filtres, la pagination, les lignes sélectionnées et le contexte « que s'est-il passé ? » sur lequel les utilisateurs comptent. Ajoutez des formulaires longs, des permissions basées sur les rôles, et des actions qui changent ce que l'UI doit autoriser, et de petites décisions d'état prennent de l'importance.
Le défi n'est pas de stocker des valeurs. C'est de garder un comportement prévisible quand plusieurs composants ont besoin de la même vérité. Si un filtre indique « Actif », le tableau, l'URL et l'action d'export doivent être d'accord. Si un utilisateur édite un enregistrement puis navigue ailleurs, l'app ne doit pas perdre silencieusement son travail. Si deux onglets sont ouverts, l'un ne doit pas écraser l'autre.
Dans Vue 3, on finit généralement par choisir entre trois endroits pour garder l'état :
- État local du composant : appartenant à un seul composant et sûr à réinitialiser quand il est démonté.
provide/inject: état partagé et scellé à une page ou une zone de fonctionnalité, sans passer par des props.- Pinia : état partagé qui doit survivre à la navigation, être réutilisé entre routes et rester facile à déboguer.
Une façon utile d'y penser : pour chaque morceau d'état, décidez où il doit vivre pour rester correct, ne pas surprendre l'utilisateur et ne pas devenir du spaghetti.
Les exemples ci-dessous s'attachent à trois problèmes courants en admin : filtres et tableaux (ce qui doit persister versus se réinitialiser), brouillons et éditions non sauvegardées (formulaires fiables), et édition multi-onglets (éviter les collisions d'état).
Une manière simple de classer l'état avant de choisir un outil
Les débats sur l'état deviennent plus faciles quand on arrête de se battre sur les outils et qu'on identifie d'abord le type d'état. Différents types d'état se comportent différemment, et les mélanger crée des bugs étranges.
Une séparation pratique :
- État UI : bascules, dialogues ouverts, lignes sélectionnées, onglets actifs, ordre de tri.
- État serveur : réponses API, indicateurs de chargement, erreurs, dernière actualisation.
- État de formulaire : valeurs des champs, erreurs de validation, flags dirty, brouillons non sauvegardés.
- État cross-screen : tout ce que plusieurs routes doivent lire ou modifier (workspace courant, permissions partagées).
Puis définissez la portée. Demandez-vous où l'état est utilisé aujourd'hui, pas où il pourrait l'être un jour. S'il ne concerne qu'un seul composant de tableau, l'état local suffit généralement. Si deux composants frères sur la même page en ont besoin, le vrai problème est le partage au niveau de la page. Si plusieurs routes en ont besoin, vous êtes dans le territoire de l'état partagé applicatif.
Ensuite, la durée de vie. Certains états doivent se réinitialiser quand on ferme un tiroir. D'autres doivent survivre à la navigation (filtres quand on regarde un enregistrement et qu'on revient). Certains doivent survivre à un rechargement (un long brouillon auquel les utilisateurs reviennent plus tard). Traiter ces trois cas de la même manière, c'est obtenir des filtres qui se réinitialisent mystérieusement ou des brouillons qui disparaissent.
Enfin, vérifiez la concurrence. Les panneaux admin rencontrent vite des cas limites : un utilisateur ouvre le même enregistrement dans deux onglets, une actualisation en arrière-plan met à jour une ligne pendant qu'un formulaire est dirty, ou deux éditeurs se précipitent pour sauvegarder.
Exemple : un écran “Utilisateurs” avec filtres, un tableau et un tiroir d'édition. Les filtres sont de l'état UI avec une durée de vie page. Les lignes sont de l'état serveur. Les champs du tiroir sont de l'état de formulaire. Si le même utilisateur est édité dans deux onglets, il faut une décision explicite de concurrence : bloquer, fusionner ou avertir.
Lorsque vous pouvez étiqueter l'état par type, portée, durée de vie et concurrence, le choix de l'outil (état local, provide/inject, ou Pinia) devient souvent beaucoup plus clair.
Comment choisir : un processus de décision qui tient la route
De bons choix d'état commencent par une habitude : décrivez l'état en mots simples avant de choisir un outil. Les panneaux admin mélangent tableaux, filtres, grands formulaires et navigation entre enregistrements, donc même un « petit » état peut devenir une source de bugs.
Un processus de décision en 5 étapes
-
Qui a besoin de l'état ?
- Un seul composant : gardez-le local.
- Plusieurs composants sous une même page : considérez
provide/inject. - Plusieurs routes : considérez Pinia.
Les filtres illustrent bien cela. S'ils n'affectent qu'un tableau qui les possède, l'état local suffit. Si les filtres vivent dans un header mais pilotent un tableau en dessous, le partage au niveau page (souvent
provide/inject) garde les choses propres. -
Combien de temps doit-il vivre ?
- Si ça peut disparaître quand le composant est démonté, l'état local est idéal.
- S'il doit survivre à un changement de route, Pinia est souvent plus adapté.
- S'il doit survivre à un rechargement, il faut aussi une persistance (storage), quel que soit l'endroit où il vit.
Cela compte surtout pour les brouillons. Les éditions non sauvegardées sont sensibles à la confiance : les gens s'attendent à retrouver un brouillon s'ils partent et reviennent.
-
Doit-il être partagé entre onglets du navigateur ou isolé par onglet ?
L'édition multi-onglets est là où se cachent les bugs. Si chaque onglet doit avoir son propre brouillon, évitez un singleton global. Préférez un état indexé par ID d'enregistrement, ou conservez-le au niveau de la page pour qu'un onglet ne puisse pas écraser un autre.
-
Choisissez l'option la plus simple qui fonctionne.
Commencez local. Montez d'un cran seulement quand la douleur est réelle : prop drilling, logique dupliquée, ou réinitialisations difficiles à reproduire.
-
Confirmez vos besoins de débogage.
Si vous avez besoin d'une vue claire et inspectable des changements entre écrans, les actions et l'état centralisés de Pinia peuvent faire gagner des heures. Si l'état est de courte durée et évident, l'état local est plus simple à lire.
État local du composant : quand c'est suffisant
L'état local est le choix par défaut quand les données n'ont d'importance que pour un composant sur une page. Il est facile d'ignorer cette option et de surconstruire un store que vous passerez des mois à maintenir.
Un cas évident : un tableau unique avec ses propres filtres. Si les filtres n'affectent qu'un seul tableau (par exemple la liste Utilisateurs) et que rien d'autre n'en dépend, gardez-les comme ref dans le composant du tableau. Il en va de même pour les petits états UI comme « le modal est-il ouvert ? », « quelle ligne est éditée ? », ou « quels éléments sont sélectionnés maintenant ? ».
Essayez de ne pas stocker ce que vous pouvez calculer. Le badge « Filtres actifs (3) » devrait être calculé à partir des valeurs de filtre courantes. Les labels de tri, résumés formatés et flags « peut enregistrer » sont aussi mieux en computed car ils restent synchronisés automatiquement.
Les règles de réinitialisation importent plus que l'outil. Décidez ce qui se purge lors d'un changement de route (généralement tout), et ce qui reste quand l'utilisateur change de vue à l'intérieur d'une même page (vous pouvez garder les filtres mais vider les sélections temporaires pour éviter des actions en masse surprenantes).
L'état local suffit généralement quand :
- L'état affecte un seul widget (un formulaire, un tableau, un modal).
- Aucun autre écran ne doit le lire ou le modifier.
- Vous pouvez le garder dans 1-2 composants sans passer des props sur plusieurs niveaux.
- Vous pouvez décrire son comportement de réinitialisation en une phrase.
La limite principale est la profondeur. Quand vous commencez à passer le même état à travers plusieurs composants imbriqués, l'état local devient du prop drilling, et c'est généralement le signal pour passer à provide/inject ou à un store.
provide/inject : partager l'état au sein d'une page ou d'une zone fonctionnelle
provide/inject se situe entre l'état local et un store complet. Un parent « fournit » des valeurs à tout ce qui est sous lui, et des composants imbriqués les « injectent » sans passer par des props. Dans les panneaux admin, c'est un excellent choix quand l'état appartient à un écran ou une zone fonctionnelle, pas à toute l'app.
Un pattern courant est une coquille de page qui possède l'état pendant que de plus petits composants le consomment : barre de filtres, tableau, barre d'actions en masse, panneau de détails, et une bannière « changements non sauvegardés ». La coquille peut fournir une petite surface réactive comme un objet filters, un objet draftStatus (dirty, saving, error), et quelques flags en lecture seule (par exemple isReadOnly basé sur les permissions).
Ce qu'il faut fournir (gardez petit)
Si vous fournissez tout, vous avez essentiellement recréé un store avec moins de structure. Fournissez seulement ce que plusieurs enfants ont vraiment besoin de lire/écrire. Les filtres sont un exemple classique : quand le tableau, les chips et l'action d'export doivent rester synchrones, il vaut mieux partager une source de vérité que jongler avec props et événements.
Clarté et pièges
Le plus grand risque est les dépendances cachées : un enfant « fonctionne juste » parce que quelque chose au-dessus fournit des données, et plus tard il est difficile de savoir d'où viennent les mises à jour.
Pour garder la lecture et les tests simples, donnez des noms clairs aux injections (souvent via des constantes ou des Symbols). Préférez aussi fournir des actions, pas seulement des objets mutables. Une petite API comme setFilter, markDirty et resetDraft rend l'appartenance et les modifications autorisées explicites.
Pinia : état partagé et mises à jour prévisibles entre écrans
Pinia brille quand le même état doit rester cohérent entre routes et composants. Dans un panneau admin, cela signifie souvent l'utilisateur courant, ses permissions, l'organisation/workspace sélectionné, et les réglages d'app. C'est pénible si chaque écran ré-implémente ces choses.
Un store aide car il donne un endroit unique pour lire et mettre à jour l'état partagé. Au lieu de passer des props à travers plusieurs couches, vous importez le store où vous en avez besoin. Quand vous passez d'une liste à une page de détail, le reste de l'UI peut réagir au même org sélectionné, permissions et réglages.
Pourquoi Pinia est souvent plus facile à maintenir
Pinia pousse une structure simple : state pour les valeurs brutes, getters pour les valeurs dérivées, et actions pour les mises à jour. Dans les UIs admin, cette structure empêche que des « quick fixes » se transforment en mutations dispersées.
Si canEditUsers dépend du rôle courant plus d'un feature flag, mettez la règle dans un getter. Si changer d'org nécessite de vider des sélections en cache et de recharger la navigation, mettez cette séquence dans une action. Vous aurez moins de watchers mystérieux et moins de « pourquoi ça a changé ? ».
Pinia fonctionne aussi bien avec Vue DevTools. Quand un bug survient, il est beaucoup plus simple d'inspecter l'état du store et de voir quelle action a tourné plutôt que de traquer des objets réactifs ad hoc créés dans des composants aléatoires.
Évitez le store fourre-tout
Un store global paraît rangé au début, puis devient un tiroir à bazar. Bons candidats pour Pinia : préoccupations vraiment partagées comme l'identité utilisateur, permissions, workspace sélectionné, feature flags et données de référence partagées entre écrans.
Les préoccupations propres à une page (comme les entrées temporaires d'un formulaire) doivent rester locales à moins que plusieurs routes n'en aient vraiment besoin.
Exemple 1 : filtres et tableaux sans tout transformer en store
Imaginez une page Commandes : un tableau, des filtres (statut, plage de dates, client), pagination et un panneau latéral qui prévisualise la commande sélectionnée. Ça devient vite confus parce qu'il est tentant de mettre chaque filtre et réglage de tableau dans un store global.
Une façon simple de choisir est de décider ce qui doit être mémorisé, et où :
- Mémoire seulement (local ou provide/inject) : se réinitialise quand vous quittez la page. Idéal pour l'état jetable.
- Query params : partageable et survit au rechargement. Bon pour filtres et pagination que l'on veut copier.
- Pinia : survit à la navigation. Bon pour « revenir à la liste exactement comme je l'ai laissée ».
Ensuite l'implémentation suit généralement :
Si personne n'attend que les réglages survivent à la navigation, gardez filters, sort, page et pageSize dans le composant de la page Orders, et laissez cette page déclencher la requête. Si la toolbar, le tableau et le panneau de prévisualisation ont besoin du même modèle et que le passage de props devient bruyant, déplacez le modèle de liste dans la coquille de page et partagez-le via provide/inject. Si vous voulez que la liste soit persistante entre routes (ouvrir une commande, partir et revenir aux mêmes filtres et sélections), Pinia est alors le bon choix.
Règle pratique : commencez local, montez à provide/inject quand plusieurs composants enfants ont besoin du même modèle, et utilisez Pinia seulement quand vous avez vraiment besoin de persistance entre routes.
Exemple 2 : brouillons et éditions non sauvegardées (formulaires que les gens peuvent croire)
Imaginez un agent support qui édite un dossier client : coordonnées, facturation et notes internes. Il est interrompu, change d'écran, puis revient. Si le formulaire oublie son travail ou sauvegarde des données à moitié complètes, la confiance est perdue.
Pour les brouillons, séparez trois choses : l'enregistrement sauvegardé le plus récent, les modifications mises en scène par l'utilisateur, et l'état UI comme les erreurs de validation.
État local : modifications mises en scène avec des règles dirty claires
Si l'écran d'édition est autonome, l'état local du composant est souvent le plus sûr. Gardez une copie draft de l'enregistrement, suivez isDirty (ou une map dirty par champ), et rangez les erreurs à côté des contrôles du formulaire.
Flux simple : chargez l'enregistrement, clonez-le en draft, éditez le draft, et n'envoyez la requête de sauvegarde que quand l'utilisateur clique Save. Cancel jette le draft et recharge.
provide/inject : un seul brouillon partagé entre sections imbriquées
Les formulaires admin sont souvent découpés en onglets ou panneaux (Profil, Adresses, Permissions). Avec provide/inject, vous pouvez garder un modèle de draft unique et exposer une petite API comme updateField(), resetDraft() et validateSection(). Chaque section lit et écrit le même draft sans passer des props sur cinq niveaux.
Quand Pinia aide pour les brouillons
Pinia devient utile quand les brouillons doivent survivre à la navigation ou être visibles en dehors de la page d'édition. Un pattern courant est draftsById[customerId], ainsi chaque enregistrement a son propre brouillon. Cela aide aussi quand les utilisateurs peuvent ouvrir plusieurs écrans d'édition.
Les bugs sur les brouillons viennent généralement de quelques erreurs prévisibles : créer un brouillon avant que l'enregistrement soit chargé, écraser un brouillon dirty lors d'un refetch, oublier de nettoyer les erreurs au cancel, ou utiliser une clé partagée qui fait que les brouillons s'écrasent entre eux. Si vous définissez des règles claires (quand créer, écraser, jeter, persister et remplacer après sauvegarde), la plupart disparaissent.
Si vous construisez des écrans admin avec AppMaster (appmaster.io), la séparation « brouillon vs enregistrement sauvegardé » s'applique toujours : gardez le brouillon côté client, et considérez le backend comme source de vérité seulement après un Save réussi.
Exemple 3 : édition multi-onglets sans collisions d'état
L'édition multi-onglets est souvent là où les panneaux admin craquent. Un utilisateur ouvre Client A, puis Client B, bascule d'un onglet à l'autre, et s'attend à ce que chaque onglet se souvienne de ses modifications non sauvegardées.
La solution est de modéliser chaque onglet comme un bundle d'état distinct, pas comme un unique brouillon partagé. Chaque onglet a besoin d'au moins une clé unique (souvent basée sur l'ID du record), des données du draft, un statut (clean, dirty, saving) et des erreurs par champ.
Si les onglets vivent dans un même écran, une approche locale suffit bien. Gardez la liste d'onglets et les brouillons possédés par le composant de la page qui rend les onglets. Chaque panneau d'édition lit et écrit seulement son propre bundle. Quand un onglet se ferme, supprimez son bundle. C'est isolé et facile à raisonner.
Peu importe où l'état vit, la forme est similaire :
- Une liste d'objets onglets (chacun avec
customerId,draft,status, eterrors) - Un
activeTabKey - Des actions comme
openTab(id),updateDraft(key, patch),saveTab(key),closeTab(key)
Pinia devient pertinent quand les onglets doivent survivre à la navigation (aller aux Commandes et revenir) ou quand plusieurs écrans doivent pouvoir ouvrir et focuser des onglets. Dans ce cas, un petit store « tab manager » maintient un comportement cohérent à travers l'app.
La principale collision à éviter est une variable globale unique comme currentDraft. Ça marche jusqu'au deuxième onglet, puis les éditions s'écrasent, les erreurs de validation s'affichent au mauvais endroit, et Save met à jour le mauvais enregistrement. Quand chaque onglet a son propre bundle, les collisions disparaissent par conception.
Erreurs courantes qui causent des bugs et du code bordélique
La plupart des bugs dans les panneaux admin ne sont pas des « bugs Vue ». Ce sont des bugs d'état : des données qui vivent au mauvais endroit, deux parties de l'écran en désaccord, ou de l'ancien état qui traîne.
Voici les patterns les plus fréquents :
Mettre tout dans Pinia par défaut rend la propriété floue. Un store global semble organisé au début, mais bientôt chaque page lit et écrit les mêmes objets, et le nettoyage devient un casse-tête.
Utiliser provide/inject sans contrat clair crée des dépendances cachées. Si un enfant injecte filters mais qu'il n'y a pas d'accord sur qui fournit et quelles actions peuvent le changer, vous aurez des mises à jour surprises quand un autre enfant commence à muter le même objet.
Mélanger état serveur et état UI dans le même store cause des écrasements accidentels. Les enregistrements fetchés se comportent différemment de « le tiroir est ouvert ? », « onglet courant » ou « champs dirty ». Quand ils vivent ensemble, un refetch peut piétiner l'UI, ou une modification UI peut muter le cache.
Négliger le nettoyage de cycle de vie laisse fuiter de l'état. Des filtres d'une vue peuvent impacter une autre, et des brouillons peuvent rester après avoir quitté la page. La prochaine fois qu'on ouvrira un autre enregistrement, on verra d'anciennes sélections et on pensera que l'app est cassée.
Mal keyer les brouillons est un tueur de confiance silencieux. Si vous stockez les brouillons sous une seule clé comme draft:editUser, éditer l'Utilisateur A puis l'Utilisateur B écrase le même brouillon.
Une règle simple empêche la plupart de ça : gardez l'état le plus proche possible de l'endroit où il est utilisé, et ne le relevez que quand deux parties indépendantes en ont vraiment besoin. Quand vous partagez, définissez la propriété (qui peut changer quoi) et l'identité (comment c'est clefé).
Une checklist rapide avant de choisir local, provide/inject ou Pinia
La question la plus utile est : qui possède cet état ? Si vous ne pouvez pas le dire en une phrase, l'état fait probablement trop de choses et devrait être scindé.
Utilisez ces vérifications comme filtre rapide :
- Pouvez-vous nommer le propriétaire (un composant, une page, ou l'app entière) ?
- Doit-il survivre aux changements de route ou à un rechargement ? Si oui, prévoyez la persistance au lieu d'espérer que le navigateur le conserve.
- Deux enregistrements seront-ils édités en même temps ? Si oui, keyez l'état par ID d'enregistrement.
- L'état est-il uniquement utilisé par des composants sous une coquille de page ? Si oui,
provide/injectconvient souvent. - Avez-vous besoin d'inspecter les changements et de savoir qui a changé quoi ? Si oui, Pinia est souvent l'endroit le plus propre pour cette portion.
Correspondance outil en termes simples :
Si l'état naît et meurt dans un composant (comme un flag dropdown ouvert/fermé), gardez-le local. Si plusieurs composants sur le même écran ont besoin d'un contexte partagé (barre de filtres + tableau + résumé), provide/inject le partage sans le rendre global. Si l'état doit être partagé entre écrans, survivre à la navigation, ou avoir des mises à jour prévisibles et débogables, utilisez Pinia et keyez les entrées par ID d'enregistrement quand des brouillons sont impliqués.
Si vous construisez une UI admin Vue 3 (y compris une générée avec des outils comme AppMaster), cette checklist vous aide à éviter de tout mettre trop tôt dans un store.
Prochaines étapes : faire évoluer l'état sans créer le bazar
La manière la plus sûre d'améliorer la gestion d'état dans les panneaux admin est de l'étendre par petites étapes banales. Commencez par l'état local pour tout ce qui reste à l'intérieur d'une page. Quand vous voyez une vraie réutilisation (logique copiée, un troisième composant qui a besoin du même état), remontez d'un niveau. Ce n'est qu'à ce moment-là qu'il faut envisager un store partagé.
Un chemin qui marche pour la plupart des équipes :
- Gardez d'abord l'état propre à la page en local (filtres, tri, pagination, panneaux ouverts/fermés).
- Utilisez
provide/injectquand plusieurs composants sur la même page ont besoin d'un contexte partagé. - Ajoutez un store Pinia à la fois pour les besoins cross-screen (gestionnaire de brouillons, gestionnaire d'onglets, workspace courant).
- Écrivez des règles de réinitialisation et respectez-les (ce qui se réinitialise à la navigation, déconnexion, Clear filters, Discard changes).
Les règles de reset semblent petites, mais elles évitent la plupart des moments « pourquoi ça a changé ? ». Décidez, par exemple, ce qui arrive à un brouillon quand quelqu'un ouvre un autre enregistrement et revient : restaurer, avertir ou réinitialiser. Puis appliquez ce comportement de façon cohérente.
Si vous introduisez un store, gardez-le centré sur la fonctionnalité. Un store de brouillons doit gérer la création, la restauration et le nettoyage des brouillons, mais il ne devrait pas non plus posséder les filtres de tableau ou les flags de layout UI.
Si vous voulez prototyper un panneau admin rapidement, AppMaster (appmaster.io) peut générer une appli web Vue3 plus backend et logique métier, et vous pouvez affiner le code généré là où vous avez besoin d'un état personnalisé. Une prochaine étape pratique est de construire un écran de bout en bout (par exemple un formulaire d'édition avec récupération de brouillon) et voir ce qui mérite Pinia versus ce qui peut rester local.
FAQ
Utilisez l'état local lorsque les données n'affectent qu'un composant et peuvent être réinitialisées quand ce composant est démonté. Exemples typiques : ouverture/fermeture d'une boîte de dialogue, lignes sélectionnées dans un seul tableau, ou une section de formulaire non réutilisée ailleurs.
Privilégiez provide/inject quand plusieurs composants sur la même page ont besoin d'une source de vérité commune et que le passage de props devient encombrant. Fournissez peu de choses et de façon intentionnelle pour garder la page facile à comprendre.
Choisissez Pinia quand l'état doit être partagé entre routes, survivre à la navigation, ou être facile à inspecter et déboguer au même endroit. Exemples fréquents : workspace courant, permissions, feature flags, et gestionnaires inter-écrans comme les brouillons ou les onglets.
Commencez par nommer le type d'état (UI, serveur, formulaire, cross-screen), puis déterminez la portée (un composant, une page, plusieurs routes), la durée de vie (se réinitialise au démontage, survit à la navigation, survit au rechargement) et la concurrence (éditeur unique ou multi-onglets). Le choix de l'outil découle généralement de ces quatre étiquettes.
Si les utilisateurs doivent partager ou restaurer la vue, mettez filtres et pagination dans les query params pour qu'ils survivent au rechargement et soient copiables. Si l'objectif est surtout « revenir à la liste telle que je l'ai laissée » entre routes, stockez le modèle de liste dans Pinia ; sinon, gardez-le au niveau de la page.
Séparez le dernier enregistrement sauvegardé des modifications mises en scène par l'utilisateur, et n'écrivez sur le backend que lors du Save. Suivez une règle claire de dirty, et décidez du comportement à la navigation (avertir, auto-save, ou conserver un brouillon récupérable) pour éviter la perte de travail.
Donnez à chaque éditeur ouvert son propre lot d'état identifié par l'ID du record (et parfois par une clé d'onglet), plutôt que d'utiliser un unique currentDraft. Cela empêche qu'une édition dans un onglet écrase celle d'un autre et évite les erreurs de validation affichées au mauvais endroit.
Un provide/inject appartenant à la page suffit si tout le flux d'édition reste confiné à une route. Si les brouillons doivent survivre à la navigation ou être accessibles en dehors de l'écran d'édition, Pinia avec quelque chose comme draftsById[recordId] est généralement plus simple et prévisible.
Ne stockez pas ce que vous pouvez calculer. Dérivez badges, résumés et indicateurs « can save » à partir de l'état courant avec des computed pour qu'ils restent synchronisés automatiquement.
Mettre tout dans Pinia par défaut, mélanger réponses serveur et toggles UI, et négliger le nettoyage à la navigation sont les causes les plus fréquentes de comportements étranges. Méfiez-vous aussi des clés pauvres comme un seul draft réutilisé pour différents enregistrements.


