# Frontend — Risques & vigilance : React Native > Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/risques/README.md` pour l'index complet. --- ## Focus ring sur `TextInput` React Native ### Risques - React Native n'a pas de pseudo-classe `:focus` — le focus visuel ne s'implémente pas en CSS - Tâche souvent marquée [x] sans vérification effective du state `focused` ### Symptômes - `TextInput` sans indication visuelle de focus → accessibilité et UX dégradées - Story marquée done mais aucun handler `onFocus`/`onBlur` présent dans le composant ### Bonnes pratiques / mitigations ```typescript const [focused, setFocused] = useState(false); setFocused(true)} onBlur={() => setFocused(false)} style={[styles.input, focused && styles.inputFocused]} /> ``` - Règle de review : toute story "focus ring" doit présenter un state `focused` + handlers + style conditionnel - Contexte technique : React Native — app-alexandrie, 25-03-2026 --- ## `ScrollView.contentInset` — propriété iOS-only ### Risques - `contentInset` n'est pas supporté sur Android — le contenu passe sous la bottom tab bar sans aucune erreur de build - Pattern fréquent quand on copie un pattern iOS existant dans un nouvel écran ### Symptômes - Sur iOS : rendu correct avec padding bottom - Sur Android : contenu masqué sous la bottom tab bar ### Bonnes pratiques / mitigations ```typescript // ❌ Anti-pattern — iOS-only, cassé sur Android // ✅ Pattern correct — cross-platform ``` - Règle de review : auditer systématiquement tout `ScrollView` avec `contentInset` dans les PR concernant des écrans avec bottom navigation - Contexte technique : React Native / react-native-safe-area-context — app-alexandrie, 25-03-2026 --- ## `fetch` sans vérification `response.ok` — erreurs non-2xx silencieuses ### Risques - Sans vérification de `response.ok`, les réponses 404/500 retournent le JSON d'erreur sans exception → le code appelant ne sait pas que la requête a échoué - En cas de proxy qui retourne du HTML sur erreur (ex : 502 Bad Gateway), `response.json()` throw une `SyntaxError` cryptique plutôt qu'une erreur métier ### Symptômes - Store qui reçoit `{ error: { code, message } }` comme si c'était une réponse valide - `SyntaxError: JSON Parse error` inexpliquée lors des erreurs réseau ### Bonnes pratiques / mitigations ```typescript const response = await fetch(url, options); const json = (await response.json()) as T; if (!response.ok) { throw json; // throw le body d'erreur structuré pour un catch cohérent } return json; ``` - Règle : tout `fetch` dans le http-client doit vérifier `response.ok` avant de retourner le JSON parsé - Contexte technique : React Native / fetch — app-alexandrie review 5.2, 27-03-2026 --- ## `accessibilityRole="summary"` sur wrapper avec enfant `header` → double annonce ### Risques - `accessibilityRole="summary"` sur un `` wrapper dont un enfant texte porte déjà `accessibilityRole="header"` : sur Android, TalkBack annonce le `accessibilityLabel` du wrapper **et** le titre enfant (double annonce). Sur iOS, `summary` est un no-op silencieux - Le dev croit renforcer l'a11y, il la dégrade ### Symptômes - Lecteur d'écran qui répète le titre de section sur Android ### Bonnes pratiques / mitigations ```tsx // ✅ le role "header" sur le titre enfant suffit à structurer la section MON COMPTE {children} ``` - Ne pas poser `accessibilityRole="summary"` sur un wrapper si le titre enfant porte déjà `accessibilityRole="header"` ; conserver uniquement `accessibilityLabel` sur le wrapper si besoin de regrouper - Contexte technique : React Native — app-alexandrie (`section-card.tsx`, IA-v2.4 H3), 27-05-2026 --- ## Bouton retour placé dans le `ScrollView` → disparaît au scroll ### Risques - Sur une app RN/Expo avec une TopBar globale sans back natif, un bouton retour ajouté comme premier enfant du `` disparaît dès que l'utilisateur défile - Critique sur une page business (gestion abonnement, résiliation) : perdre l'accès au retour pendant le scroll est un piège UX direct ### Symptômes - Le back est visible en haut de page puis introuvable une fois scrollé ### Bonnes pratiques / mitigations ```tsx {/* flex:1, background */} …Retour {/* contenu */} ``` - Le header local sticky vit **hors** du ScrollView (View parent + ScrollView frère) - Ne PAS doubler `insets.top` (le header le consomme → `contentInset.top = 0`) ; ne PAS mettre le back dans le `contentContainerStyle` - Si `` Expo Router avec `headerShown: true`, préférer le header natif (`headerBackVisible: true`) - Test défensif : vérifier statiquement qu'un `` n'est pas dans un `` - Contexte technique : React Native / Expo Router — app-alexandrie (ux-cleanup-2, `subscription/manage`), 28-05-2026 --- ## Pull-to-refresh mobile web : préférer une lib battle-test à un PTR custom ### Risques - Un PTR custom (écouter `pointerdown`/`pointermove` sur un wrapper) est intrinsèquement fragile : le navigateur mobile **préempte** les touch events pour son scroll natif dès que la page est en haut → les `pointerdown`/`pointermove` du composant sont absents ou annulés par `pointercancel` - Chrome Android a SON PTR natif qui déclenche un vrai `location.reload()` : sur une PWA standalone, ça vide l'état Pinia/Redux et affiche le SW cache (souvent une version pré-login) — l'utilisateur croit s'être déconnecté - Un wrapper PTR casse les layouts grid (`display: contents` rend `place-items` inopérant, `display: block` brise le centrage) ### Symptômes - PTR custom OK en simulation desktop, inopérant sur device ; ou rechargement natif affichant le contenu pré-login ### Bonnes pratiques / mitigations ```css /* AVANT toute autre tentative : neutraliser le PTR natif Chrome Android */ body { overscroll-behavior-y: contain; } /* Chrome 63+/Safari 16+/Firefox 59+ */ ``` - Préférer une lib battle-test (`pulltorefreshjs`, ~6 kB) qui s'attache à `` (zéro impact layout grid) et gère iOS/Android/desktop ; API `PullToRefresh.init({ mainElement, onRefresh })`, destroy au unmount - Indicateur : SVG inline avec viewBox contrôlé, pas un glyphe unicode (rendu variable selon la font) - **Tester sur vrai device** Android ET iOS (DevTools "device toolbar" ne reproduit pas la préemption native) - Contexte technique : Vue 3.5 / Vite / PWA Workbox — RL799_V2 (4 itérations custom ratées → `pulltorefreshjs`), 11-05-2026