# 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 : 09-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)

---

## Règle d’or

Si ce n’est pas **confirmé comme fonctionnel et utile**,  
**ça n’a 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 d’exigence que le backend.

---

## Format standard d’un pattern (obligatoire)

## Pattern : <Nom clair et précis>

- Objectif : ce que le pattern résout
- Contexte : type d’application / contraintes
- Quand l’utiliser : 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 d’une vue.
- **Contexte** : SPA ou webapp consommant des données asynchrones (API, backend, cache).
- **Quand l’utiliser** : dès qu’une vue dépend de données externes ou d’un 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 d’erreur compréhensible pour l’utilisateur
- [ ] É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 l’utiliser** : dès que l’application 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 l’app 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 l’utiliser** : dès qu’un formulaire dépasse un simple champ isolé.
- **Quand l’éviter** : formulaires ultra-simples sans validation réelle.

### Analyse

- **Avantages** :
  - UX claire (l’utilisateur sait quoi corriger)
  - Moins d’erreurs 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 d’erreur 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

---

### 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 d’architecture 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 n’entre pas encore ici
