Files
_Assistant_Lead_Tech/10_frontend_patterns_valides.md
2026-03-16 15:36:53 +01:00

402 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Patterns front-end validés
Ce fichier contient **uniquement** des patterns front-end :
- testés,
- validés,
- utilisés dans des projets réels (ou des apps complètes, pas des snippets isolés).
Il sert de **mémoire durable** pour éviter :
- de refaire les mêmes erreurs,
- de redélibérer éternellement sur des sujets déjà tranchés,
- de propager des “bonnes pratiques” théoriques non éprouvées.
Dernière mise à jour : 16-03-2026
---
## Index
- [Gestion explicite des états UI (loading / empty / error)](#pattern-etats-ui-loading-empty-error)
- [Séparation claire server state / client state](#pattern-separation-server-state-client-state)
- [Formulaire robuste avec validation et erreurs explicites](#pattern-formulaire-robuste)
- [Navigation réactive post-action async (React / Expo Router)](#pattern-navigation-reactive-post-action-async)
- [Refresh idempotent sur store de liste paginée](#pattern-refresh-idempotent-liste-paginee)
- [UI admin légère sur domaine existant](#pattern-ui-admin-legere-domaine-existant)
---
## Règle dor
Si ce nest pas **confirmé comme fonctionnel et utile**,
**ça na rien à faire ici**.
- Pas de conseils vagues
- Pas de patterns “à la mode”
- Pas de dépendance implicite à un framework ou une version non précisée
---
## Périmètre couvert
- SPA et webapps
- UX technique (forms, erreurs, loading, feedback)
- State management (client / server)
- Architecture front-end
- Performance et accessibilité
- Sécurité front (au niveau applicatif)
- DX et maintenabilité
Ce fichier traite le **front-end comme un logiciel en production**,
au même niveau dexigence que le backend.
---
## Format standard dun pattern (obligatoire)
## Pattern : <Nom clair et précis>
- Objectif : ce que le pattern résout
- Contexte : type dapplication / contraintes
- Quand lutiliser : cas pertinents
- Quand léviter : contre-exemples
- Avantages : bénéfices concrets
- Limites / vigilance : pièges, dette potentielle
- Validé le : DD-MM-YYYY
- Contexte technique : framework + version + tooling principal
### Implémentation (exemple minimal)
```txt
(contenu volontairement minimal, lisible, non-magique)
```
---
<a id="pattern-etats-ui-loading-empty-error"></a>
## Pattern : Gestion explicite des états UI (loading / empty / error)
### Synthèse
- **Objectif** : éviter les interfaces ambiguës ou incohérentes en rendant explicites tous les états possibles dune vue.
- **Contexte** : SPA ou webapp consommant des données asynchrones (API, backend, cache).
- **Quand lutiliser** : dès quune vue dépend de données externes ou dun traitement async.
- **Quand léviter** : vues purement statiques ou synchrones sans dépendance externe.
### Analyse
- **Avantages** :
- UX plus prévisible et compréhensible
- Debug facilité (état visible = problème identifiable)
- Base saine pour tests et accessibilité
- **Limites / vigilance** :
- Peut sembler verbeux sur des écrans simples
- Nécessite une discipline pour ne pas “court-circuiter” les états
### Validation
- Validé le : 25-01-2026
- Contexte technique : SPA (React / Vue / Svelte agnostique), API HTTP
### Implémentation (exemple minimal)
```txt
if (loading) {
afficher un skeleton ou spinner
} else if (error) {
afficher un message clair + action possible
} else if (data est vide) {
afficher un état empty explicite
} else {
afficher la vue nominale
}
```
### Checklist
- [ ] Aucun écran blanc ou silencieux
- [ ] Message derreur compréhensible pour lutilisateur
- [ ] États testables individuellement
- [ ] Accessibilité respectée (focus, lecture écran)
- [ ] Pas de logique métier cachée dans le rendu
---
<a id="pattern-separation-server-state-client-state"></a>
## Pattern : Séparation claire server state / client state
### Synthèse
- **Objectif** : éviter le mélange des responsabilités entre données serveur et état local UI.
- **Contexte** : SPA ou webapp consommant une API avec interactions utilisateur.
- **Quand lutiliser** : dès que lapplication affiche des données distantes modifiables ou synchronisées.
- **Quand léviter** : applications très simples ou purement statiques.
### Analyse
- **Avantages** :
- Logique plus lisible et testable
- Réduction des bugs liés aux états incohérents
- Évolutivité facilitée quand lapp grossit
- **Limites / vigilance** :
- Demande de la rigueur dans le découpage
- Peut sembler abstrait au début pour des petits projets
### Validation
- Validé le : 25-01-2026
- Contexte technique : SPA agnostique (React / Vue / Svelte), API HTTP
### Implémentation (exemple minimal)
```txt
serverState = données venant du backend (fetch, cache, sync)
clientState = état local UI (filtres, onglets, modales, formulaires)
Ne jamais :
- stocker du state UI dans le cache serveur
- dériver la logique UI directement des réponses API sans adaptation
```
### Checklist
- [ ] Les données serveur peuvent être invalidées / rechargées
- [ ] Létat UI est local et réinitialisable
- [ ] Les responsabilités sont lisibles dans le code
- [ ] Les tests peuvent cibler chaque type détat
- [ ] Pas de dépendance implicite entre UI et API
---
<a id="pattern-formulaire-robuste"></a>
## Pattern : Formulaire robuste avec validation et erreurs explicites
### Synthèse
- **Objectif** : garantir des formulaires fiables, compréhensibles et maintenables.
- **Contexte** : toute interface avec saisie utilisateur et règles métier.
- **Quand lutiliser** : dès quun formulaire dépasse un simple champ isolé.
- **Quand léviter** : formulaires ultra-simples sans validation réelle.
### Analyse
- **Avantages** :
- UX claire (lutilisateur sait quoi corriger)
- Moins derreurs silencieuses
- Base saine pour tests et accessibilité
- **Limites / vigilance** :
- Peut sembler verbeux sans discipline
- Risque de duplication si mal factorisé
### Validation
- Validé le : 25-01-2026
- Contexte technique : Front-end agnostique, API HTTP
### Implémentation (exemple minimal)
```txt
- Validation côté client (format, champs requis)
- Validation côté serveur (règles métier)
- Mapping explicite des erreurs serveur → champs UI
- Aucun submit silencieux
```
### Checklist
- [ ] Messages derreur compréhensibles et localisés
- [ ] Validation client + serveur cohérente
- [ ] Focus automatique sur le champ en erreur
- [ ] États loading / disabled gérés
- [ ] Tests sur cas valides et invalides
---
<a id="pattern-navigation-reactive-post-action-async"></a>
## Pattern : Navigation réactive post-action async (React / Expo Router)
### Synthèse
- **Objectif** : déclencher la navigation après une action asynchrone (login, register, submit) de façon idiomatique et sans bypasser la réactivité React.
- **Contexte** : SPA ou app mobile React avec state management (Zustand, Redux, Context) et router déclaratif (React Router, Expo Router, Next.js App Router).
- **Quand l'utiliser** : dès qu'une navigation dépend du résultat d'une action async.
- **Quand l'éviter** : navigations synchrones sans état async impliqué.
### Analyse
- **Avantages** :
- Respecte le cycle de vie React (pas de lecture de state hors cycle)
- Re-render automatique si l'état change entre-temps
- Testable : on peut assert sur l'état, pas sur des effets de bord
- **Limites / vigilance** :
- Ne pas oublier les dépendances du `useEffect` (ESLint react-hooks/exhaustive-deps)
- Gérer le cas "composant démonté" si la navigation peut être annulée
### Validation
- Validé le : 07-03-2026
- Contexte technique : React 18+ / Zustand / Expo Router — pattern applicable sur React Router, Next.js App Router
### Implémentation (exemple minimal)
```typescript
// ❌ Anti-pattern : lecture de state hors cycle React
const handleSubmit = async () => {
await login(email, password);
const { accessToken } = useAuthStore.getState(); // bypasse la réactivité
if (accessToken) router.replace('/(tabs)');
};
// ✅ Pattern correct : useEffect réactif sur le state
const { accessToken, isLoading, error } = useAuthStore();
useEffect(() => {
if (accessToken && !isLoading && !error) {
router.replace('/(tabs)');
}
}, [accessToken, isLoading, error]);
const handleSubmit = async () => {
await login(email, password);
// la navigation se déclenche via useEffect quand le store se met à jour
};
```
### Pour les callbacks OAuth (ref nécessaire)
```typescript
// Quand un callback externe déclenche la navigation
const pendingOAuth = useRef(false);
useEffect(() => {
if (pendingOAuth.current && accessToken) {
pendingOAuth.current = false;
router.replace('/(tabs)');
}
}, [accessToken]);
const handleOAuth = async () => {
pendingOAuth.current = true;
await exchangeWithIdp(token);
};
```
### Checklist
- [ ] Aucun `store.getState()` utilisé pour lire l'état post-action dans un handler
- [ ] `useEffect` avec dépendances explicites (state pertinent + isLoading + error)
- [ ] Cas d'erreur géré (ne pas naviguer si error est défini)
- [ ] `useRef` si le trigger vient d'un callback externe (OAuth, deep link)
- [ ] Convention documentée dans la story foundations / project-context avant les premiers écrans
---
<a id="pattern-refresh-idempotent-liste-paginee"></a>
## Pattern : Refresh idempotent sur store de liste paginée
### Synthèse
- **Objectif** : garantir quun pull-to-refresh recharge une liste paginée sans doublons, sans courses réseau et sans état intermédiaire incohérent.
- **Contexte** : app mobile ou SPA avec store de domaine (ex. Zustand) et pagination incrémentale.
- **Quand lutiliser** : dès quune même liste supporte à la fois `loadMore` et `refresh`.
- **Quand léviter** : listes purement statiques ou données entièrement remplacées sans pagination.
### Analyse
- **Avantages** :
- évite les doublons lors des refresh concurrents
- garde une transition atomique entre ancien et nouvel état
- rend le comportement async testable côté store
- **Limites / vigilance** :
- impose une discipline claire entre `refresh` et `loadMore`
- demande une clé didentité stable pour dédupliquer les items
### Validation
- Validé le : 10-03-2026
- Contexte technique : React Native / Expo / Zustand / listes paginées
### Implémentation (exemple minimal)
```txt
- conserver une promesse de refresh partagée tant quun refresh est en vol
- refuser ou réutiliser tout refresh concurrent au lieu den lancer un second
- remplacer atomiquement la liste à la fin du refresh
- dédupliquer les items par identifiant au merge des pages suivantes
- empêcher `loadMore` de fusionner sur un snapshot devenu obsolète
```
### Checklist
- [ ] Une seule promesse de refresh en vol à la fois
- [ ] `refresh` et `loadMore` ont des garde-fous explicites
- [ ] La liste est remplacée atomiquement après refresh
- [ ] Les pages suivantes sont dédupliquées par identifiant stable
- [ ] Tests sur refresh concurrent + refresh suivi de pagination
---
<a id="pattern-ui-admin-legere-domaine-existant"></a>
## Pattern : UI admin légère sur domaine existant
### Synthèse
- **Objectif** : ajouter une capacité interne simple sans ouvrir trop tôt un back-office séparé ni dupliquer la logique métier.
- **Contexte** : app mobile ou SPA avec un domaine métier déjà structuré et quelques actions internes ponctuelles.
- **Quand lutiliser** : publication, activation, modération légère, bascule de statut, action opérateur simple.
- **Quand léviter** : permissions complexes, workflows multiples, audit riche ou volume dactions qui justifie un vrai espace dadministration.
### Analyse
- **Avantages** :
- réutilise le service et le store métier existants
- limite le coût de structure pour une capacité admin mince
- garde les mutations testables et lisibles
- **Limites / vigilance** :
- ne pas laisser cette approche dériver vers un pseudo back-office implicite
- le refresh après mutation doit être explicite sur les vues impactées
### Validation
- Validé le : 10-03-2026
- Contexte technique : React Native / Expo Router / store de domaine
### Implémentation (exemple minimal)
```txt
- ajouter une route dédiée minimale pour laction interne
- réutiliser le service/store métier existant au lieu de créer une couche parallèle
- afficher le statut courant avant action
- bloquer les actions concurrentes avec un flag explicite (`isUpdating*`)
- déclencher un refresh explicite des vues impactées après succès
- éviter les mutations en fire-and-forget
```
### Checklist
- [ ] Route dédiée minimale, pas de mini back-office générique
- [ ] Réutilisation du domaine métier existant
- [ ] Garde-fou explicite contre les doubles actions
- [ ] Refresh explicite après mutation réussie
- [ ] Tests sur succès, erreur et action concurrente
---
### Principes transverses
- Un pattern = une responsabilité claire
- On privilégie la simplicité locale avant la généricité globale
- Le code doit rester compréhensible 6 mois plus tard
- Si un pattern devient central → il mérite une décision darchitecture dédiée
## Notes importantes
- 3 bons patterns > 30 moyens
- Si un pattern évolue :
- on met à jour la date
- on précise le nouveau contexte
- En cas de doute → le pattern nentre pas encore ici