Files
_Assistant_Lead_Tech/knowledge/frontend/risques/react-native.md
T
MaksTinyWorkshop 5f5c87296e docs(knowledge): capitalisation frontend — intégration du triage local (mai-juin 2026)
Triage et intégration des propositions frontend du buffer 95_a_capitaliser.md
(lot local RL799_V2/Vue3 + app-alexandrie/RN-Expo, mai-juin 2026).

~73 entrées intégrées sur knowledge/frontend/ + 1 nouveau fichier, dont :
- patterns/state.md : race-token partagé latest-wins (fusion 3 props), capture sync anti-race,
  event bus timestamp, clé cache composite, état dérivé = computed
- risques/state.md : 9 risques Zustand/store (fetchId reset, useRef remount, re-fetch infini
  sur [], flag optimiste écrasé, cache détail/liste stale, latch sans reset, :key index)
- patterns/navigation.md : Expo Router (tab bar, useFocusEffect, Href typé, routing pur fusionné)
- patterns/general.md : helpers temps purs, composants génériques + skeleton, fail-fast, touch target
- risques/general.md : 24 risques (sweep statique, filtre client liste paginée, hooks avant return,
  a11y VoiceOver/disabled, redirection allowlist, RangeError toISOString, section i18n...)
- design-tokens (cluster theming light/dark MD3), tests, performance, react-native, nextjs
- NOUVEAU risques/responsive.md (gating par capacité d'input + checklist régressions mobile)
- READMEs patterns/risques mis à jour

Doublons inter-fichiers évités (vérifié : aucune ancre dupliquée introduite).
Rejets (doublons 91/9/87), reciblages workflow (156/257) et bloc 32 (CLAUDE projet) non intégrés ici.
Source 95_a_capitaliser.md non purgée (purge en fin de capitalisation complète).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:31:53 +02:00

7.2 KiB

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

const [focused, setFocused] = useState(false);

<TextInput
  onFocus={() => 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

// ❌ Anti-pattern — iOS-only, cassé sur Android
<ScrollView contentInset={{ bottom: insets.bottom }}>

// ✅ Pattern correct — cross-platform
<ScrollView contentContainerStyle={{ paddingBottom: insets.bottom }}>
  • 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

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 <View> 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

// ✅ le role "header" sur le titre enfant suffit à structurer la section
<View accessibilityLabel="Mon compte">
  <Text accessibilityRole="header">MON COMPTE</Text>
  {children}
</View>
  • 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 <ScrollView> 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

<View style={styles.root}>                       {/* flex:1, background */}
  <View style={[styles.headerBar, { paddingTop: insets.top }]}>
    <Pressable onPress={handleBack} hitSlop={8}>Retour</Pressable>
  </View>
  <ScrollView contentInset={{ top: 0 }}>{/* contenu */}</ScrollView>
</View>
  • 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 <Stack.Screen> Expo Router avec headerShown: true, préférer le header natif (headerBackVisible: true)
  • Test défensif : vérifier statiquement qu'un <Pressable accessibilityLabel="Retour"> n'est pas dans un <ScrollView>
  • 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

/* 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 à <body> (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