Gestion d'état
Chaque type d’état a une place désignée. On ne mélange pas.
| Type d’état | Où | Exemple |
|---|---|---|
| État serveur (données API) | TanStack Query (core/query) |
useHealthQuery(), requêtes des features |
| État de session / auth | core/auth (snapshot useSyncExternalStore) |
useCurrentUser(), useCan() |
| État d’URL (params, query, hash) | TanStack Router (search params, path params) | filtre de liste partageable, pagination |
| État de formulaire | TanStack Form (@tanstack/react-form) |
login, formulaires des features |
| État local transitoire d’un composant | useState |
panneau ouvert/fermé, champ non persisté |
TanStack Query
Section intitulée « TanStack Query »- Singleton
queryClientdanscore/query/query-client.ts(une instance pour tout le cycle de vie ; le router SSR-query l’utilise aussi). - Defaults :
retrylimité aux erreurs réseau (le wrapper API gère déjà le refresh 401),refetchOnWindowFocus: false,staleTime: 30 s, mutations sans retry automatique. - Clés de requêtes via
createQueryKeys(scope):
const contratsKeys = createQueryKeys('residence.contrats')queryClient.invalidateQueries({ queryKey: contratsKeys.all })queryClient.setQueryData(contratsKeys.detail(id), data)- Invalidation ciblée après une mutation (jamais
invalidateQueries()global).
- L’état serveur ne se duplique jamais dans
useState: le cache Query est la source.refetch()/setQueryDatapour rafraîchir. - Une feature n’instancie pas de
QueryClient(le singleton suffit) et déclare ses clés viacreateQueryKeys. - L’état URL se lit/écrit via les search params du router — pas de
useStatesynchronisé à la main.
À ne pas faire
Section intitulée « À ne pas faire »- ❌ État global “au cas où” (contexte fourre-tout) sans justification.
- ❌ Lire plusieurs fois une même requête avec des options différentes (cache incohérent) — centraliser dans un hook de feature.
- ❌ Synchroniser
useStateavec une requête (useEffect+setState) au lieu d’utiliser le cache Query.
