mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-05-18 08:18:15 +02:00
Triage du 95_a_capitaliser.md (~75 propositions) : - 60 entrées intégrées dans knowledge/ (backend, frontend, workflow) - 4 nouveaux fichiers : backend/patterns/tests.md, backend/risques/tests.md, frontend/patterns/general.md, workflow/patterns/general.md - 6 doublons rejetés - Mise à jour des READMEs index pour refléter les nouvelles entrées - 95_a_capitaliser.md restauré à sa structure initiale - 40_decisions_et_archi.md : décision mono-tenant déployable vs SaaS multi-tenant - 90_debug_et_postmortem.md : sub-agents Write indisponible, effet iceberg CI, prisma migrate diffs cosmétiques Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.9 KiB
9.9 KiB
title: Frontend — Patterns : Navigation
domain: frontend
bucket: patterns
tags: [navigation, react, expo-router, useeffect, async]
applies_to: [analysis, implementation, review]
severity: medium
validated_on: 2026-03-21
source_projects: [app-template-resto, app-alexandrie]
Pattern : Factorisation page mode dynamique via
Frontend — Patterns : Navigation
Extrait de la base de connaissance Lead_tech. Voir
knowledge/frontend/patterns/README.mdpour l'index complet.
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
- Ne pas oublier les dépendances du
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)
// ❌ 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)
// 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 useEffectavec dépendances explicites (state pertinent + isLoading + error)- Cas d'erreur géré (ne pas naviguer si error est défini)
useRefsi le trigger vient d'un callback externe (OAuth, deep link)- Convention documentée dans la story foundations / project-context avant les premiers écrans
Pattern : Intégration tierce en mode link-out — préférer une page locale canonique
Synthèse
- Objectif : éviter les parcours concurrents et centraliser les garde-fous UX quand une fonctionnalité publique dépend d'un service tiers externe.
- Contexte : site ou webapp avec CTA publics menant vers un tiers de réservation, paiement, prise de rendez-vous ou formulaire externe.
- Quand l'utiliser : dès qu'une fonctionnalité externe dispose d'une page locale dédiée côté produit (
/reservation,/booking, etc.). - Quand l'éviter : si le produit assume volontairement une sortie directe unique vers le tiers, sans page locale intermédiaire ni besoin de contextualisation.
Analyse
- Avantages :
- UX plus cohérente entre home, navigation et pages dédiées
- garde-fous, wording et fallbacks centralisés au même endroit
- facilite l'évolution future vers embed, click-to-load ou variantes de parcours
- Limites / vigilance :
- ajoute une étape intermédiaire si la page locale n'apporte aucune valeur
- demande de maintenir l'alignement entre les CTA internes et le parcours canonique
Validation
- Validé le : 19-03-2026
- Contexte technique : site web public / intégration tierce en mode lien externe
Implémentation (exemple minimal)
- faire pointer les CTA internes (home, nav, landing) vers une page locale dédiée
- faire de cette page locale le point canonique vers le service tiers externe
- centraliser sur cette page le contexte utile, les garde-fous et les fallbacks
- éviter les sorties directes concurrentes vers le tiers depuis plusieurs endroits du site
Checklist
- Un parcours canonique unique est défini
- Les CTA internes convergent vers la page locale dédiée
- Les garde-fous et fallbacks sont centralisés
- Les sorties directes concurrentes vers le tiers sont évitées ou justifiées
Pattern : Click-to-load strict pour les embeds tiers (iframe/widget)
Synthèse
- Objectif : ne charger aucun service tiers sans action explicite de l'utilisateur (performance + consentement implicite).
- Contexte : site/webapp avec modules de réservation, map, chat ou tout embed iframe à la demande.
- Quand l'utiliser : dès qu'un embed tiers est chargé à la demande (pas au premier rendu).
- Quand l'éviter : si l'embed est central à la page et doit être visible immédiatement.
Analyse
- Avantages :
- LCP non pollué par des tiers (performance-first)
- Aucun tiers ne reçoit de données utilisateur sans action volontaire (consentement implicite)
- Fallback toujours disponible en cas d'erreur iframe
- Limites / vigilance :
- Le fallback (lien externe +
tel:) doit être actionnable même si l'embed échoue
- Le fallback (lien externe +
Validation
- Validé le : 21-03-2026
- Contexte technique : React / Next.js — app-template-resto
Implémentation
const [loaded, setLoaded] = useState(false);
const [errored, setErrored] = useState(false);
if (errored) return <a href={url}>Ouvrir {label}</a>;
return (
<>
{!loaded && <button onClick={() => setLoaded(true)}>Charger {label}</button>}
{loaded && <iframe src={url} onError={() => setErrored(true)} />}
</>
);
Pattern : Overlay/drawer accessible avec focus trap
Synthèse
Teleport + backdrop + Escape + scroll-lock n'est pas suffisant : le focus trap est obligatoire.
Analyse
Sans focus trap, le clavier peut sortir de la sheet/panel et casser l'ordre de navigation.
Validation
- Validé le : 09-04-2026
- Contexte technique : Vue 3 / accessibilité overlays — RL799_V2
- Applicable à tout drawer/sheet/modal custom en SPA (desktop et mobile clavier).
Implémentation
- Capturer le focus à l'ouverture (premier élément interactif).
- Boucler Tab/Shift+Tab dans le conteneur.
- Restaurer le focus au trigger à la fermeture.
Pattern : Factorisation page mode dynamique via route.meta.mode typé
Synthèse
- Objectif : factoriser un composant Vue qui partage 95 % de sa logique entre plusieurs routes ne différant que par le wording, sans recourir à
route.name(fragile) ou query string (manipulable). - Contexte : projet Vue avec deux routes (ex :
/invitation+/reset-password) qui partagent la mécanique technique stricte (validation token + saisie mot de passe + consume) et ne diffèrent que par le wording. - Quand l'utiliser : factorisation justifiée par un partage > 90 % de logique entre routes.
- Quand l'éviter : routes qui diffèrent fonctionnellement au-delà du wording — préférer deux composants distincts.
Analyse
- Avantages :
- explicite, déclaratif, type-checkable
- augmenter
RouteMetadansvue-routerpour qu'un mode manquant produise une erreur build
- Limites / vigilance :
- wording centralisé en un seul
computed— pas dev-iféparpillés dans le template - endpoint dynamique en un seul
if/elsedans le handler
- wording centralisé en un seul
Validation
- Validé le : 28-04-2026
- Contexte technique : Vue 3 / vue-router — RL799_V2
Implémentation
// router/index.ts
{
path: '/invitation',
name: 'invitation',
component: ResetPasswordPage,
meta: { requiresGuest: true, mode: 'invitation' as const },
},
{
path: '/reset-password',
name: 'reset-password',
component: ResetPasswordPage,
meta: { requiresGuest: true, mode: 'reset' as const },
},
// ResetPasswordPage.vue
const pageMode = computed<PageMode>(() => {
const meta = route.meta as { mode?: PageMode } | undefined;
return meta?.mode === 'invitation' ? 'invitation' : 'reset';
});
const wording = computed(() => {
if (pageMode.value === 'invitation') {
return { eyebrow: 'Bienvenue', title: 'Définissez votre mot de passe', /* … */ };
}
return { eyebrow: 'Réinitialisation', title: '...', /* … */ };
});
const handleSubmit = async () => {
if (pageMode.value === 'invitation') {
await consumeInvitationToken(token.value, newPassword.value);
} else {
await consumeResetToken(token.value, newPassword.value);
}
// Post-consume uniforme : redirige /login pour les deux modes
};
Règles d'or
meta.modetypé en literal union ('invitation' | 'reset')- A11y obligatoire : autofocus sur le champ password au mount post-validation,
<label for>associés,<AppMessage variant="error">avecrole="alert",<AppMessage variant="success">avecaria-live="polite" - Test obligatoire : monter la page avec chaque
meta.modeet vérifier que le wording correspond
Anti-patterns
- Détecter le mode via
route.name(fragile, couplage implicite, signal d'intention faible) - Détecter via query string (pollue URL, manipulable)