Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

⚛️ React – Installation, Hooks & Déploiement

Guide complet IDEO‑Lab pour la bibliothèque UI JavaScript (Hooks, VDOM, JSX, Routage).

1.1

Vue d'ensemble

Bibliothèque UI, Virtual DOM, Déclaratif.

Bibliothèque UI VDOM
1.2

JSX (Syntaxe)

HTML dans JavaScript, className, {}.

JSX Babel
1.3

Installation (Vite)

npm create vite@latest (moderne), CRA (ancien).

Vite npm HMR
2.1

Composants (Function)

Composants fonctionnels (vs Classes).

Function Composant
2.2

Props (Propriétés)

Passer des données (Parent -> Enfant).

Props Destructuring
2.3

State (useState)

Gérer l'état local, useState, immutabilité.

useState State Hooks
3.1

Lifecycle (useEffect)

Effets de bord, data fetching, cleanup.

useEffect Lifecycle
3.2

Événements & Formulaires

onClick, onChange, "Controlled Components".

onClick onChange
3.3

Rendu (Listes & Conditions)

.map() (key prop), && (ternaire).

.map() key
4.1

Data Fetching (API)

fetch/Axios dans useEffect, React Query.

fetch Axios React Query
4.2

Routage (React Router)

BrowserRouter, Routes, Route, Link.

React Router Link
4.3

State: useContext

createContext, Provider, useContext.

useContext Prop Drilling
4.4

State Global (Avancé)

Redux (Toolkit) & Zustand.

Redux Zustand
5.1

Build & Déploiement

npm run build, Vercel/Netlify, Nginx.

build Vercel Nginx
6.1

Cheat-sheet (Hooks)

useState, useEffect, useContext, useRef.

cheat Hooks
React Project Initializer — Wizard (Vite) + commandes prêtes à copier
Paramètres du projet
Astuce: kebab-case recommandé.
Le wizard génère des commandes “best practice” (Vite + options). Tu peux ensuite coller dans ton terminal.
Commandes générées

Copie/colle tout le bloc. Ajuste si nécessaire (ex: Node version, proxy API, etc.).

Clique sur “Générer” 👈

Snippet optionnel : Proxy API (dev)

Si ton backend est sur http://localhost:8000, tu peux proxyfier via Vite pour éviter CORS en dev.

/* vite.config.js (extrait)
                export default defineConfig({
                server: {
                proxy: {
                "/api": "http://localhost:8000"
                }
                }
                });
                */
React — Addons & Plugins incontournables (les plus populaires)
1) Routing (SPA & navigation)

Quand ton app dépasse 1 page, tu as besoin d’un routeur côté client (URLs propres, nested routes, layouts, params…).

LibPourquoi c’est populaireQuand l’utiliserInstall
react-router-domStandard de facto, API v6 solideSPA classiquenpm i react-router-dom
@tanstack/routerRouter typé, moderne, proche TanStackApps TS exigeantesnpm i @tanstack/router
// Exemple "minimum"
                    import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
2) Data fetching, cache & “server state”

En prod, gérer fetch/loading/error/cache à la main est coûteux. Les libs de “server state” sont un énorme gain de productivité.

LibPoints fortsCas idéalInstall
@tanstack/react-queryCache, refetch, pagination, mutationsStandard modernenpm i @tanstack/react-query
SWRTrès simple, “stale-while-revalidate”Apps Next.js / simple datanpm i swr
AxiosInterceptors, ergonomie HTTPAuth headers/refreshnpm i axios
Règle pro : React Query = serveur-state ; Redux/Zustand = client-state.
3) State global (client state)

Quand l’état est partagé partout (UI state, session, prefs) : un store évite les props à rallonge et structure la logique.

LibStylePourquoi populaireInstall
@reduxjs/toolkitEnterprisePrévisible, DevTools, conventionsnpm i @reduxjs/toolkit react-redux
ZustandMinimalisteTrès léger, API hooks, selectorsnpm i zustand
JotaiAtomsGranularité fine, simplenpm i jotai
RecoilAtoms/graphHistorique, concept “graph state”npm i recoil
// Zustand (extrait)
                    import { create } from "zustand";
                    export const useStore = create(set => ({ count: 0, inc: () => set(s => ({count: s.count+1})) }));
4) Forms, validation & UX

Les formulaires sont un gros sujet (perf, validation, erreurs, schema, champs dynamiques). Les libs spécialisées font gagner énormément de temps.

LibPoints fortsStack “classique”Install
react-hook-formPerf, uncontrolled-friendly, simpleRHF + Zod/Yupnpm i react-hook-form
FormikHistorique, stableLegacy / équipes habituéesnpm i formik
ZodSchema TS-firstValidation + parsingnpm i zod
YupSchema validationFormik-friendlynpm i yup
// RHF + Zod (mini)
                    import { useForm } from "react-hook-form";
                    import { z } from "zod";
5) UI kits, composants & styling

Pour livrer vite : UI kits + design system. Pour custom : Tailwind + primitives.

LibRôlePourquoi populaireInstall
Tailwind CSSUtility-first CSSRapide, cohérent, DXnpm i -D tailwindcss
MUI (Material UI)UI kit completEnterprise, composants richesnpm i @mui/material
Ant DesignUI kitDashboards, tables, composantsnpm i antd
Chakra UIUI kitAPI simple, accessiblenpm i @chakra-ui/react
shadcn/uiComposants “copy-paste”Basé Radix, très modernenpx shadcn-ui@latest init
Radix UIPrimitives accessiblesBase solide pour design systemnpm i @radix-ui/react-*
Framer MotionAnimationsTrès puissant, simplenpm i framer-motion
Combo très populaire : Tailwind + shadcn/ui + Radix + Framer Motion.
6) Tables, charts & virtualisation

Le “pain” en dashboard : tables, tri, filtres, pagination, charts, performance listes.

LibUsagePoint fortInstall
@tanstack/react-tableTables headlessTri, filtres, pagination, perfnpm i @tanstack/react-table
AG GridTable enterpriseFeatures massivesnpm i ag-grid-react
RechartsChartsSimple, efficacenpm i recharts
Chart.jsChartsLarge écosystèmenpm i chart.js
react-windowVirtualisationListes très longuesnpm i react-window
@tanstack/react-virtualVirtualisationTrès moderne, flexiblenpm i @tanstack/react-virtual
// Virtualisation (idée)
                    import { FixedSizeList as List } from "react-window";
7) Tooling, tests, qualité & dev experience
CatégorieLib / outilPourquoi importantInstall
BuildViteDev server rapide + build modernenpm create vite@latest
LintESLintQualité code, règles Reactnpm i -D eslint
FormatPrettierStyle cohérent, reviews rapidesnpm i -D prettier
Tests unitVitestRapide, intégré Vitenpm i -D vitest
Tests UITesting LibraryTest “comme l’utilisateur”npm i -D @testing-library/react
E2EPlaywrightTests navigateur modernesnpm i -D @playwright/test
QualitéTypeScriptRéduit bugs, meilleure DXnpm i -D typescript
ErrorsSentryMonitoring prodnpm i @sentry/react
Stack tests “clean” : Vitest + Testing Library + Playwright.
8) Auth, i18n, analytics, misc (plugins très fréquents)
BesoinLib populairePourquoiInstall
i18nreact-i18nextStandard i18n, namespacesnpm i i18next react-i18next
AuthAuth0 React SDK / Firebase AuthLogin, tokens, sessionsnpm i @auth0/auth0-react
Datesdate-fnsLéger, utilitairenpm i date-fns
Iconslucide-react / react-iconsDX, cohérencenpm i lucide-react
Notificationsreact-hot-toastToasts élégantsnpm i react-hot-toast
AnalyticsPlausible / GA wrapperMesure usagenpm i plausible-tracker
SEO (SPA)react-helmet-asyncMeta tags dynamiquesnpm i react-helmet-async
Sélection “starter pro” (stack recommandée)
Core:
                    - react-router-dom
                    - @tanstack/react-query
                    - zustand (ou @reduxjs/toolkit)
                    - react-hook-form + zod

                    UI:
                    - tailwindcss
                    - shadcn/ui (+ radix)
                    - framer-motion

                    Quality:
                    - typescript
                    - eslint + prettier
                    - vitest + testing-library
                    - playwright
                    - sentry
Objectif : livrer vite + qualité prod + maintenabilité.
Phrase à retenir
“Choisis une lib par problème (routing, server-state, forms),
                    pas une lib parce qu’elle est à la mode.”
1.1 Vue d'ensemble : React (déclaratif, composants, VDOM & rendu)
React : la promesse

React est une bibliothèque UI centrée sur un principe simple : l’interface est une fonction de l’état. Tu écris des composants (fonctions) qui retournent une UI (JSX) en fonction des props et du state. Quand l’état change, React calcule la différence et met à jour le DOM de façon contrôlée.

Ce que React fait bien
  • Composition : assembler une UI complexe avec des briques simples.
  • State management local : rendre la UI prédictible (au lieu de manipuler le DOM à la main).
  • Écosystème : routing, data fetching, forms, state global, SSR, tooling…
  • Performance pratique : updates ciblées + scheduling moderne (selon versions).
Ce que React ne fait pas “tout seul”
  • Router (React Router / Next Router…)
  • Data fetching (fetch/axios, TanStack Query, loaders…)
  • State global (Redux, Zustand, Jotai, Context+Reducer…)
  • Build tool (Vite, Next.js, etc.)
React = UI runtime. Le “framework” vient souvent de l’assemblage (Next.js, Remix, etc.).
Équation mentale
UI = f(props, state)

                    - props : données "entrantes" (parent → enfant), immuables côté enfant
                    - state : données "locales" (interactions), modifiables via setState/useState
                    - render : recalcul de l'arbre UI
                    - commit : application minimale des changements au DOM réel
Déclaratif vs Impératif : pourquoi ça change tout

En mode impératif, tu “pilotes” le DOM : tu cibles des éléments et tu les modifies. En mode déclaratif (React), tu décris l’UI attendue pour un état donné ; React s’occupe des mises à jour.

Impératif (style jQuery / DOM API)
// "Trouve la div, change le texte, cache le bouton, ..."
                            // Risque : état dispersé et incohérent ("UI drift")

                            const el = document.getElementById("count");
                            el.textContent = String(count);

                            if(count > 3){
                            document.getElementById("cta").style.display = "none";
                            }

Problème typique : tu finis avec un état “réel” dans le DOM + un état “logique” dans le JS, et ils divergent avec le temps.

Déclaratif (React)
function Counter(){
                            const [count, setCount] = React.useState(0);

                            return (
                            <div>
                            <p id="count">{count}</p>
                            {count <= 3 && (
                            <button id="cta" onClick={() => setCount(c => c + 1)}>
                            +1
                            </button>
                            )}
                            </div>
                            );
                            }

Ici, l’UI est toujours cohérente avec count : un seul “source of truth”.

3 bénéfices majeurs
  • Lisibilité : on comprend l’UI en lisant le composant.
  • Testabilité : on teste des sorties (UI) pour des entrées (state/props).
  • Robustesse : moins de logique DOM “au fil de l’eau”.
Composants : fonctions, état, composition

Un composant React est (souvent) une fonction pure-ish : elle lit props + state, et retourne une description d’UI. La puissance vient de la composition (petites briques).

Props : contrat d’API d’un composant
function PriceTag({ amount, currency = "EUR" }){
                            return <strong>{amount} {currency}</strong>;
                            }

                            // parent:
                            <PriceTag amount={29.9} currency="EUR" />
  • Les props descendent (parent → enfant).
  • Un enfant ne modifie pas ses props.
State : interaction & dynamique locale
function SearchBox(){
                            const [q, setQ] = React.useState("");

                            return (
                            <input
                            value={q}
                            onChange={(e) => setQ(e.target.value)}
                            placeholder="Search..."
                            />
                            );
                            }
  • State = “mémoire” du composant.
  • On met à jour via setters (setQ), pas via mutation brute.
Composition (pattern clé)
function Page(){
                    return (
                    <Layout>
                    <Header />
                    <Main>
                    <ProductsGrid />
                    </Main>
                    <Footer />
                    </Layout>
                    );
                    }
En React, on gagne en “architecture” en pensant API de composants + composition, pas en empilant des classes utilitaires et du DOM manuel.
Virtual DOM (VDOM) : pourquoi ça existe

Manipuler le DOM réel est coûteux (layout, paint, reflow). React crée une représentation en mémoire (arbre) et compare l’ancien et le nouveau pour ne mettre à jour que le nécessaire.

Réconciliation (reconciliation) : la mécanique
  1. State/props change → React relance le rendu du sous-arbre concerné.
  2. Nouvel arbre (description UI) en mémoire.
  3. Diffing : comparaison ancien/nouveau arbre.
  4. Commit : application minimale au DOM réel.
State change (count=1)
                    |
                    ▼
                    Render (new tree)
                    |
                    ▼
                    Diff old/new
                    |
                    ▼
                    Commit (patch minimal DOM)

                    Ex:
                    Ancien: <p>0</p>
                    Nouveau: <p>1</p>
                    → patch: textContent seulement
Point ultra important : les key en listes

Pour aider React à “matcher” les éléments d’une liste entre deux rendus, on donne une key stable. Sans key stable, React peut réutiliser des nœuds “au mauvais endroit” → bugs UI subtils (inputs, focus, animations).

{items.map(item => (
                    <Row key={item.id} item={item} />   // ✅ key stable
                    ))}

                    // ❌ éviter key=index si la liste peut être réordonnée / filtrée
Le pipeline de rendu : Render phase vs Commit phase

React sépare conceptuellement : Render (calcul de ce que l’UI devrait être) et Commit (application au DOM). C’est essentiel pour comprendre perf, effets, et comportements “concurrents” selon versions.

Render phase (calcul)
  • Exécution des composants (fonctions) pour produire l’arbre UI.
  • Peut être rejouée (en dev strict mode, ou selon scheduling).
  • Doit rester “pure-ish” : pas d’effets de bord (API calls, mutations globales).
// Render doit être "safe":
                            function Comp(){
                            // ✅ calcul
                            const x = heavyCompute();

                            // ❌ éviter: side effects ici (fetch, localStorage write, etc.)
                            return <div>...</div>;
                            }
Commit phase (application)
  • React met à jour le DOM réel.
  • Les effects (useEffect) s’exécutent après commit.
  • Les “layout effects” (useLayoutEffect) s’exécutent avant paint.
React.useEffect(() => {
                            // ✅ side effects ici:
                            // fetch, subscriptions, analytics...
                            return () => {/* cleanup */}
                            }, [deps]);
Conséquence pratique
  • Si tu mets des effets de bord dans le rendu, tu crées des comportements imprévisibles.
  • Les effets doivent être idempotents et nettoyables (cleanup) → robustesse.
Mental model “Senior” (ce qu’il faut vraiment retenir)
1) Single source of truth
  • L’état doit vivre à un endroit clair (local, context, store).
  • Éviter de dupliquer le même état dans 3 composants.
2) Data down, actions up
  • Props descendent.
  • Les événements remontent via callbacks (ou store).
3) Immutabilité (pragmatique)
  • On ne mute pas “à la main” les structures d’état → on crée de nouvelles références.
  • Pourquoi : comparaison par référence, memoization, predictibilité.
4) Performance = mesurer, pas deviner
  • Optimiser trop tôt (useMemo/useCallback partout) = complexité inutile.
  • Le vrai levier : architecture UI, découpage composants, keys, listes, virtualisation.
5) “Side effects” au bon endroit
  • Rendu = calcul pur-ish.
  • Effets = useEffect/useLayoutEffect, avec deps propres + cleanup.
Si tu maîtrises state placement, composition, keys/lists et effects, tu as déjà 80% du “Senior React”.
Mini check-list “qualité”
QuestionPourquoiSignal d’alerte
Où vit le state ?Lisibilité, partage, évite duplicationsState copié dans plusieurs composants
Keys stables en listes ?Réconciliation correctekey=index sur liste triable/filtrable
Effects propres ?Évite fuites, double calls, bugsuseEffect sans deps / sans cleanup
Perf mesurée ?Optimisations pertinentesuseMemo/useCallback partout sans profiler
1.2 JSX : syntaxe, règles, pièges & bonnes pratiques (Babel / TSX)
JSX = “HTML dans JS”… mais pas vraiment

JSX (JavaScript XML) est une extension de syntaxe qui te permet d’écrire une UI lisible sous forme de balises dans du JavaScript. Ce n’est pas du HTML : c’est du sucre syntaxique transformé (transpilé) par un outil (ex: Babel, Vite, SWC) en appels de fonctions qui construisent une description d’UI.

Ce que JSX produit (idée)
// JSX
                            const el = <button className="btn">OK</button>;

                            // (concept) transpilation
                            // el = React.createElement("button", { className: "btn" }, "OK");

Le point important : JSX décrit un arbre. React le “réconcilie” ensuite avec l’arbre précédent.

Pourquoi c’est utile
  • Lisibilité : UI proche du résultat visuel.
  • Composition : composants = balises réutilisables.
  • Contraintes saines : règles explicites (className, htmlFor, etc.).
  • Interop : tu peux mixer logique + UI sans “string templates”.
JSX est plus proche d’un AST UI que d’un template HTML.
Règles de base (à connaître par cœur)
1) Un seul élément racine
// ✅ Fragment (pas de div inutile)
                            return (
                            <>
                            <Header />
                            <Main />
                            </>
                            );
2) Toutes les balises doivent être fermées
// ✅ self-closing
                            <img src="/logo.png" alt="logo" />
                            <input value={q} onChange={...} />

                            // ❌ invalide en JSX
                            // <img src="/logo.png">
3) Attributs “JS friendly”
  • classclassName
  • forhtmlFor
  • Attributs en camelCase : onClick, tabIndex, readOnly
4) Commentaires JSX
return (
                            <div>
                            {/* commentaire JSX */}
                            <p>Hello</p>
                            </div>
                            );
5) Attributes booléens
<input disabled />          // ✅ true
                            <input disabled={false} />   // ✅ false (explicite)
Le JSX te force à être explicite : c’est un avantage (moins de magie, moins de bugs).
Expressions { } : ce qui est autorisé, ce qui ne l’est pas

Dans JSX, { } accepte une expression JavaScript (valeur), pas une suite d’instructions. Si tu as besoin de logique, calcule avant le return ou fais une fonction.

OK : expressions
const name = "Alice";
                            const isPro = true;

                            return (
                            <div>
                            <h3>Hello {name.toUpperCase()}</h3>
                            <p>Plan: {isPro ? "Pro" : "Free"}</p>
                            </div>
                            );
Rendu conditionnel : patterns
// 1) AND (afficher si vrai)
                            {isOpen && <Modal />}

                            // 2) Ternaire (if/else UI)
                            {status === "ok" ? <Ok /> : <Error />}

                            // 3) Early return (souvent le plus clair)
                            if (loading) return <Spinner />;
Attention : valeurs qui “s’affichent”
// ⚠️ 0 s'affiche (car 0 est une valeur rendable)
                            {items.length && <p>Items</p>}

                            // ✅ solution
                            {items.length > 0 && <p>Items</p>}
Ce qui ne marche pas : statements
// ❌ pas de if directement dans JSX
                            return (
                            <div>
                            { if(isPro){ <Pro /> } }  // invalide
                            </div>
                            );

                            // ✅ faire avant
                            let content = isPro ? <Pro /> : <Free />;
                            return <div>{content}</div>;
Listes, map() et la règle d’or : key stable

En React, les listes doivent toujours avoir une key stable (id métier), pas l’index, sinon tu risques des bugs subtils (inputs qui “échangent” leur valeur, focus perdu, animations qui glitch).

✅ Bonne liste
{users.map(u => (
                            <li key={u.id}>
                            <strong>{u.name}</strong>
                            </li>
                            ))}
  • key doit être unique et stable (ne change pas si tri/filtre).
  • Évite Math.random() : ça casse la réconciliation (tout re-mount).
❌ Problèmes courants
// ❌ key=index (si la liste peut bouger)
                            {items.map((it, i) => (
                            <Row key={i} item={it} />
                            ))}

Key=index peut être OK uniquement si la liste est statique (pas de tri, pas d’insert, pas de filtre).

Pattern : render “vide” proprement
{items.length === 0
                            ? <EmptyState />
                            : <List items={items} />}
Events, forms, refs : le trio du “vrai web”
Events : handlers propres
function Button(){
                            const handleClick = (e) => {
                            e.preventDefault();
                            // ...action
                            };

                            return (
                            <button onClick={handleClick}>Save</button>
                            );
                            }
Forms : controlled input
function Login(){
                            const [email, setEmail] = React.useState("");

                            return (
                            <input
                            value={email}
                            onChange={(e) => setEmail(e.target.value)}
                            placeholder="email"
                            />
                            );
                            }
Ref : accès DOM (avec parcimonie)
function FocusMe(){
                            const ref = React.useRef(null);

                            React.useEffect(() => {
                            ref.current?.focus();
                            }, []);

                            return <input ref={ref} />;
                            }
Le DOM direct est l’exception : préfère l’état + props. Utilise ref pour focus, mesure, intégration lib.
ClassName dynamique (pattern)
const cls = `btn ${isActive ? "btn--active" : ""}`;
                    return <button className={cls}>OK</button>;

En pratique, beaucoup utilisent une lib (ex: clsx) pour éviter les concaténations.

Pièges fréquents + règles “senior”
Pièges JSX
  • Inline objects : style= recrée un objet à chaque render (souvent OK, mais attention en perf).
  • Functions inline partout : pas grave par défaut, mais peut impacter memoization.
  • DangerouslySetInnerHTML : risque XSS si contenu non sanitizé.
  • Fragments : pense à <> pour éviter DOM inutile.
Injection HTML (danger)
// ⚠️ XSS si html non contrôlé
                            
                            <div dangerouslySetInnerHTML={{ __html: html }} />
                            
                        
Conventions de code (lisibilité)
  • Limiter JSX “trop profond” : extraire en composants.
  • Pré-calculer variables avant le return (cls, flags, formatted text).
  • Éviter ternaires imbriqués → préférer “early return” ou fonctions.
  • Nommer clairement les handlers : handleSubmit, onSave
Le JSX “propre” : peu de logique dans le markup, beaucoup de composition.
Perf : règle simple
1) D'abord: rendre la UI claire et correcte
                    2) Ensuite: profiler (React DevTools Profiler)
                    3) Puis: optimiser le hotspot (memo, split, virtualization)
TSX (TypeScript) : JSX + types = robustesse
Props typées
type UserCardProps = {
                            user: { id: string; name: string };
                            onSelect?: (id: string) => void;
                            };

                            function UserCard({ user, onSelect }: UserCardProps){
                            return (
                            <button onClick={() => onSelect?.(user.id)}>
                            {user.name}
                            </button>
                            );
                            }
Children & ReactNode
type PanelProps = {
                            title: string;
                            children: React.ReactNode;
                            };

                            function Panel({ title, children }: PanelProps){
                            return (
                            <section>
                            <h3>{title}</h3>
                            <div>{children}</div>
                            </section>
                            );
                            }

En TSX, tu sécurises l’API des composants (ce qui est un vrai “senior move” sur un gros projet).

Patterns TSX utiles
  • Union types pour états: "idle" | "loading" | "error" | "success"
  • Props discriminantes pour composants multi-variantes
  • Types “readonly” sur données immuables
1.3 Installation React — Vite (moderne), tooling & setup “pro”
Pourquoi Vite est devenu le standard

Vite est un outil de build / dev server moderne conçu pour éliminer les lenteurs historiques des bundlers classiques. En dev, il s’appuie sur les ES Modules natifs du navigateur ; en prod, il bundle efficacement.

Avantages clés
  • Démarrage instantané (pas de bundle complet au lancement).
  • HMR ultra rapide (rechargement à chaud quasi immédiat).
  • Config légère (fonctionne “out of the box”).
  • Écosystème moderne (plugins, TS, JSX, CSS).
Positionnement
  • Vite = tooling frontend (pas un framework).
  • Parfait pour SPA React “classique”.
  • Base idéale avant d’aller vers Next / Remix.
Aujourd’hui : nouveau projet React = Vite par défaut.
Installation React avec Vite (pas à pas)
# 1) Créer le projet
                    npm create vite@latest

                    # Questions interactives :
                    # ✔ Project name: my-react-app
                    # ✔ Framework: React
                    # ✔ Variant: JavaScript ou TypeScript

                    # 2) Aller dans le projet
                    cd my-react-app

                    # 3) Installer les dépendances
                    npm install

                    # 4) Lancer le serveur de dev
                    npm run dev

Le serveur démarre généralement sur http://localhost:5173.

Avec TypeScript (recommandé)
npm create vite@latest my-react-app -- --template react-ts
Choisir TS dès le départ évite une migration coûteuse plus tard.
Structure d’un projet React + Vite
my-react-app/
                    ├── public/                # Fichiers statiques (favicon, robots.txt)
                    ├── src/
                    │   ├── assets/            # Images, fonts, styles globaux
                    │   ├── components/        # Composants UI réutilisables
                    │   ├── pages/             # Pages / vues (si SPA)
                    │   ├── hooks/             # Hooks custom
                    │   ├── services/          # API / data access
                    │   ├── App.jsx            # Composant racine
                    │   ├── main.jsx           # Point d’entrée React
                    │   └── index.css
                    ├── index.html             # Template HTML racine
                    ├── package.json           # Dépendances & scripts
                    ├── vite.config.js         # Configuration Vite
                    └── README.md

En React, l’architecture du dossier = lisibilité du projet. Un senior structure très tôt (pages, services, hooks).

Scripts NPM & HMR
# package.json (extrait)
                    "scripts": {
                    "dev": "vite",
                    "build": "vite build",
                    "preview": "vite preview"
                    }
npm run dev
  • Serveur de développement.
  • HMR : modification instantanée sans reload page.
  • Idéal pour itérations rapides.
npm run build / preview
  • Build de production (assets optimisés).
  • preview = simuler la prod localement.
Toujours tester npm run build avant un merge important.
vite.config.js (bases utiles)
import { defineConfig } from "vite";
                    import react from "@vitejs/plugin-react";

                    export default defineConfig({
                    plugins: [react()],
                    server: {
                    port: 5173,
                    open: true
                    },
                    build: {
                    sourcemap: true
                    }
                    });
Cas courants
  • Proxy API backend (dev) → éviter CORS.
  • Alias de chemins (@/components).
  • Variables d’environnement (import.meta.env).
CRA, Vite, Next, Remix : qui fait quoi ?
OutilStatutUsage
ViteStandard moderneSPA React rapide, flexible
Create React AppLegacyÀ éviter pour nouveaux projets
Next.jsFrameworkSSR, SSG, routing, fullstack
RemixFrameworkData-driven, routing avancé

Règle simple : Vite pour SPA, Next/Remix pour app web “complète”.

Setup “Senior” dès le jour 1
  • Choisir TypeScript.
  • Installer ESLint + Prettier.
  • Structurer components / pages / services / hooks.
  • Configurer alias de chemins.
  • Prévoir scripts lint et typecheck.
# Exemple outillage minimal
                    npm install -D eslint prettier
                    npm install axios
                    npm install react-router-dom
Un bon setup initial évite 80 % de dette technique frontend.
2.1 Composants React — Function Components (standard) vs Class (legacy)
Un composant = une brique UI réutilisable

En React, un composant est une unité de rendu et de logique UI. Il reçoit des props (entrées), peut gérer du state (état local), et retourne du JSX (sortie UI). Les composants s’assemblent par composition.

Ce qu’un composant encapsule
  • Structure (markup JSX)
  • Style (className/CSS modules/Tailwind…)
  • Comportement (handlers, state, effects)
  • Contrat (API de props)
Deux grandes familles
  • Function components (standard moderne) : hooks, plus simple, plus composable.
  • Class components (legacy) : lifecycle methods, maintenance d’ancien code.
Aujourd’hui : on écrit presque tout en function components. Les classes servent surtout à lire/maintenir du legacy.
Mental model
Component(props) -> JSX

                    - props : entrée (paramètres)
                    - state : mémoire interne
                    - hooks : “outils” pour gérer state, effets, memo, refs
                    - composition : assembler une UI à partir de composants
Function components (moderne) : la base

Un function component est une fonction JS/TS qui retourne du JSX. La logique “state & lifecycle” est gérée via hooks (useState, useEffect, etc.).

Composant simple (stateless)
function Badge({ label }) {
                            return <span className="badge">{label}</span>;
                            }
Composant avec état
function Counter() {
                            const [count, setCount] = React.useState(0);

                            return (
                            <div>
                            <p>{count}</p>
                            <button onClick={() => setCount(c => c + 1)}>+1</button>
                            </div>
                            );
                            }
Pourquoi c’est mieux que les classes (en général)
  • Moins de boilerplate (pas de this, pas de constructors).
  • Composition de logique via hooks custom (réutilisation réelle).
  • Lisibilité : props + state + rendu au même endroit.
  • Interop : hooks modernes, libs modernes.
Le “senior move” : réduire la taille des composants, extraire la logique dans des hooks custom, et clarifier l’API de props.
Règles importantes (Hooks)
  • Appeler les hooks uniquement au top-level (pas dans des if/loops).
  • Les hooks ne doivent être appelés que dans des components ou hooks custom.
  • Le rendu doit rester “pur-ish” : pas d’effets de bord dans le return.
Props, children & composition : le vrai pouvoir de React
Props = contrat
function Button({ variant = "primary", onClick, children }) {
                            const cls = `btn btn--${variant}`;
                            return (
                            <button className={cls} onClick={onClick}>
                            {children}
                            </button>
                            );
                            }
  • children = contenu “slot” (composition).
  • Defaults = API ergonomique.
  • Props doivent être stables et prévisibles.
Composition (Layout / Slots)
function Card({ title, actions, children }) {
                            return (
                            <section className="card">
                            <header>
                            <h3>{title}</h3>
                            <div>{actions}</div>
                            </header>
                            <div className="card__body">{children}</div>
                            </section>
                            );
                            }

Pattern “slots” : tu passes des éléments React en props (actions) → très flexible sans complexité.

Data down, actions up
Parent -> props -> Child
                    Child -> callback/event -> Parent

                    Ex: <SearchBox value={q} onChange={setQ} />
Class components (legacy) : quand et comment

Les classes étaient le standard avant les hooks. On les rencontre encore en maintenance. Elles gèrent l’état via this.state et le cycle de vie via des méthodes dédiées.

Exemple minimal
class CounterLegacy extends React.Component {
                            state = { count: 0 };

                            componentDidMount() {
                            // side effects (subscribe / fetch...)
                            }

                            componentWillUnmount() {
                            // cleanup
                            }

                            render() {
                            return (
                            <div>
                            <p>{this.state.count}</p>
                            <button onClick={() => this.setState({ count: this.state.count + 1 })}>
                            +1
                            </button>
                            </div>
                            );
                            }
                            }
Lifecycle : mapping mental vers hooks
ClasseHooks (équivalent)But
componentDidMountuseEffect(() => {}, [])init / fetch / subscribe
componentDidUpdateuseEffect(() => {}, [deps])réagir à un changement
componentWillUnmountreturn cleanup() dans useEffectdésinscription / cleanup
En legacy : attention au this, bindings et aux effets de bord dispersés.
Quand utiliser encore une classe ?
  • Projet existant majoritairement en classes (cohérence).
  • Refactor progressif (migration vers hooks par étapes).
  • Cas rarissimes : error boundaries legacy (aujourd’hui mieux gérés via APIs modernes selon stack).
Patterns “Senior” (design de composants)
1) Séparer “container” et “presentational”
function UsersPage(){
                            const { data, isLoading } = useUsers();   // data + side effects
                            if(isLoading) return <Spinner />;
                            return <UsersList users={data} />;        // UI pure-ish
                            }
  • Container : data, appels API, orchestration.
  • Presentational : UI pure, props → JSX.
2) Custom hooks = réutilisation réelle
function useDebouncedValue(value, ms){
                            const [v, setV] = React.useState(value);
                            React.useEffect(() => {
                            const t = setTimeout(() => setV(value), ms);
                            return () => clearTimeout(t);
                            }, [value, ms]);
                            return v;
                            }
3) API stable (ergonomie)
  • Props simples + defaults.
  • Éviter “god-components” avec 25 props.
  • Nommer props comme une API produit : onClose, isOpen, variant.
4) Inversion of control (slots)
<Card
                            title="Billing"
                            actions={<Button variant="ghost">Export</Button>}
                            >
                            <BillingTable />
                            </Card>
Un bon composant = “petit”, testable, et son API rend les usages évidents.
Anti-patterns & performance : ce qui coûte cher en production
Anti-patterns
  • God component : 800 lignes, tout mélangé (data + UI + effects).
  • State dupliqué : même info stockée à plusieurs endroits.
  • Props drilling extrême : 8 niveaux de props inutiles (solution : context/store, mais avec mesure).
  • Effects mal cadrés : deps incorrectes → loops, sur-fetch, fuite mémoire.
Perf : leviers réels
  • Découper composants pour limiter rerenders “en cascade”.
  • Virtualiser les longues listes (1000+ rows).
  • Éviter re-mount (keys stables).
  • Profiler avant d’ajouter memo/useMemo/useCallback.
Règle senior:
                            Profiler → identifier hotspot → optimiser
                            Pas l’inverse.
Trop de memoization peut ralentir et compliquer. Le gain vient d’abord du découpage + architecture.
Checklist “qualité composant” (review rapide)
PointQuestionSignal d’alerte
API propsEst-ce clair, minimal, cohérent ?25 props / noms incohérents
StateVit-il au bon niveau ?Duplication / lifting non maîtrisé
Side effectsuseEffect propre (deps + cleanup) ?sur-fetch, loops, fuite mémoire
CompositionPeut-on réutiliser via children/slots ?composant rigide, non composable
PerfProblème prouvé au profiler ?memo partout “au cas où”
AccessibilitéSemantic HTML, aria si besoin ?div partout, pas de label
Un composant “pro” = API nette + state bien placé + effects clean + composition.
2.2 Props — flux de données, API de composants & patterns avancés
Flux de données unidirectionnel (fondamental)

Les props sont le mécanisme standard pour transmettre des données d’un composant parent vers un composant enfant. Ce flux est unidirectionnel : on ne “remonte” jamais les données directement.

Parent → Enfant
function App(){
                            const user = { id: 1, name: "Alice", role: "admin" };

                            return (
                            <UserCard
                            name={user.name}
                            role={user.role}
                            isAdmin={user.role === "admin"}
                            />
                            );
                            }
Enfant (lecture seule)
function UserCard(props){
                            // ❌ interdit : mutation des props
                            // props.name = "Bob";

                            return (
                            <div>
                            <strong>{props.name}</strong>
                            <em>{props.role}</em>
                            </div>
                            );
                            }
Les props sont immutables. Si quelque chose doit changer, le state vit plus haut.
Règle d’or
Parent = source de vérité
                    Enfant = rendu + callbacks
Destructuring : syntaxe propre et lisible

Plutôt que d’accéder à props.xxx partout, on déstructure les props directement dans la signature du composant.

Sans destructuring
function Badge(props){
                            return <span>{props.label}</span>;
                            }
Avec destructuring (recommandé)
function Badge({ label }){
                            return <span>{label}</span>;
                            }
Valeurs par défaut
function Button({ variant = "primary", disabled = false }){
                    return (
                    <button className={`btn-${variant}`} disabled={disabled}>
                    OK
                    </button>
                    );
                    }
Les defaults rendent l’API du composant plus robuste et plus agréable à utiliser.
children : composition plutôt que configuration

children est une prop spéciale qui contient tout ce qui est passé entre les balises d’un composant. C’est la clé de la composition React.

Utilisation côté parent
<Card>
                            <h3>Titre</h3>
                            <p>Contenu libre</p>
                            </Card>
Implémentation côté composant
function Card({ children }){
                            return (
                            <section className="card">
                            {children}
                            </section>
                            );
                            }
Slots “avancés” via props
function Modal({ title, actions, children }){
                    return (
                    <div className="modal">
                    <header>
                    <h3>{title}</h3>
                    {actions}
                    </header>
                    <main>{children}</main>
                    </div>
                    );
                    }

Pattern très “senior” : API flexible sans multiplier les props primitives.

Callbacks : faire remonter une action

Les props servent aussi à transmettre des fonctions. C’est ainsi qu’un enfant peut notifier le parent d’un événement.

Parent
function App(){
                            const [count, setCount] = React.useState(0);

                            return (
                            <Counter
                            value={count}
                            onIncrement={() => setCount(c => c + 1)}
                            />
                            );
                            }
Enfant
function Counter({ value, onIncrement }){
                            return (
                            <div>
                            <p>{value}</p>
                            <button onClick={onIncrement}>+1</button>
                            </div>
                            );
                            }
Convention de nommage
  • onClick, onChange, onClose → événements
  • handleX → handlers internes
Pattern clé : data down, actions up.
Props complexes : objets, tableaux, render props
Objets & tableaux
function UserList({ users }){
                            return (
                            <ul>
                            {users.map(u => (
                            <li key={u.id}>{u.name}</li>
                            ))}
                            </ul>
                            );
                            }

Attention aux références : si users change à chaque render, le composant re-rendera (souvent OK, parfois à mesurer).

Render props (pattern avancé)
function DataFetcher({ render }){
                            const data = { value: 42 };
                            return render(data);
                            }

                            // Usage
                            <DataFetcher render={(d) => <span>{d.value}</span>} />

Moins utilisé aujourd’hui (souvent remplacé par hooks), mais important à comprendre.

Typage des props : sécurité & documentation
TypeScript (recommandé)
type ButtonProps = {
                            variant?: "primary" | "secondary";
                            disabled?: boolean;
                            onClick: () => void;
                            };

                            function Button({ variant = "primary", disabled, onClick }: ButtonProps){
                            return (
                            <button disabled={disabled} onClick={onClick}>
                            OK
                            </button>
                            );
                            }
PropTypes (legacy JS)
Button.propTypes = {
                            variant: PropTypes.string,
                            disabled: PropTypes.bool,
                            onClick: PropTypes.func.isRequired,
                            };

PropTypes reste utile en JS pur, mais TS est devenu le standard.

Des props typées = moins de bugs + meilleure DX.
Patterns senior & pièges courants
Bonnes pratiques
  • API de props minimale et cohérente.
  • Préférer children / slots à des flags multiples.
  • Documenter implicitement via types et noms clairs.
Props drilling

Passer une prop sur 6 niveaux est parfois un smell. Solutions possibles : lifting, composition, context (avec parcimonie).

Pièges
  • Muter un objet reçu en prop (❌).
  • Multiplier les booléens : isSmall, isDark, isRounded
  • Props trop génériques (data, config fourre-tout).
Si un composant a trop de props, il fait probablement trop de choses.
Checklist rapide
QuestionPourquoiRed flag
Les props sont-elles immuables ?PrévisibilitéMutation directe
API claire ?LisibilitéNoms vagues
Callbacks bien nommés ?Intention claireFonctions anonymes confuses
children utilisé à bon escient ?CompositionFlags multiples inutiles
2.3 State & useState — état local, immutabilité, patterns & pièges
Le State = la “mémoire” vivante du composant

Le state représente les données dynamiques d’un composant : celles qui peuvent changer suite à une interaction, un événement, ou un effet. Contrairement aux props (venues de l’extérieur), le state est local.

Exemples typiques de state
  • Valeur d’un input / formulaire
  • Compteur, toggle, onglet actif
  • État de chargement (loading)
  • Résultat d’un fetch (si local au composant)
Règles fondamentales
  • Modifier le state ⇒ re-render du composant (et de ses enfants).
  • Le state est persistant entre deux renders.
  • Le state doit être minimal (pas de duplication inutile).
Question clé : “Est-ce que cette donnée doit vraiment être mémorisée ?”
Mental model
Render = f(props, state)

                    state change
                    ↓
                    re-render
                    ↓
                    UI mise à jour
useState : API de base

useState est le hook qui permet d’ajouter un état local à un composant fonctionnel. Il retourne toujours un couple : la valeur courante et une fonction de mise à jour.

Exemple simple
import { useState } from "react";

                            function Counter(){
                            const [count, setCount] = useState(0);

                            return (
                            <div>
                            <p>{count}</p>
                            <button onClick={() => setCount(count + 1)}>+1</button>
                            </div>
                            );
                            }
Ce qui se passe
  1. Premier render → count = 0
  2. Clic → setCount(1)
  3. React déclenche un nouveau render
  4. UI reflète la nouvelle valeur
Valeur initiale “lourde”
// Lazy init (calcul exécuté une seule fois)
                    const [data, setData] = useState(() => expensiveInit());

Pattern utile pour éviter des calculs coûteux à chaque render.

Immutabilité : règle NON négociable

React détecte les changements de state par référence. Muter directement un objet ou un tableau empêche React de voir le changement.

❌ Mauvais (mutation directe)
const [user, setUser] = useState({ name: "Alice", age: 30 });

                            const birthday = () => {
                            user.age += 1;      // mutation
                            setUser(user);     // même référence
                            };

React peut ne pas re-render → bug silencieux.

✅ Bon (copie immuable)
const [user, setUser] = useState({ name: "Alice", age: 30 });

                            const birthday = () => {
                            setUser({
                            ...user,
                            age: user.age + 1
                            });
                            };
Arrays : patterns sûrs
// Ajouter
                    setItems(prev => [...prev, newItem]);

                    // Supprimer
                    setItems(prev => prev.filter(i => i.id !== id));

                    // Mettre à jour
                    setItems(prev =>
                    prev.map(i => i.id === id ? { ...i, done: true } : i)
                    );
Mises à jour, batching & fonctions de mise à jour

Les mises à jour de state peuvent être batchées. Il est donc dangereux de se baser sur une valeur “courante” lorsqu’on fait plusieurs updates successives.

❌ Problème potentiel
setCount(count + 1);
                            setCount(count + 1); // peut rester à +1
✅ Fonction de mise à jour
setCount(prev => prev + 1);
                            setCount(prev => prev + 1); // +2 garanti
Règle senior : dès qu’un update dépend de l’ancien state → utiliser la forme fonctionnelle.
Où placer le state ? (question clé d’architecture)
State local
  • Utilisé par un seul composant
  • Ex: toggle, input, dropdown
State levé (lifting state up)
  • Partagé par plusieurs enfants
  • Vit dans le parent commun
// Lifting state up
                    function Parent(){
                    const [value, setValue] = useState("");

                    return (
                    <>
                    <Input value={value} onChange={setValue} />
                    <Preview value={value} />
                    </>
                    );
                    }

Le bon placement du state simplifie tout le reste (props, perf, tests).

Patterns “Senior” autour du state
1) State minimal
  • Ne stocker que la donnée source.
  • Dériver le reste au render.
// ❌ inutile
                            const [fullName, setFullName] = useState("Alice Doe");

                            // ✅ mieux
                            const fullName = `${firstName} ${lastName}`;
2) Séparer logique & UI
function useCounter(){
                            const [count, setCount] = useState(0);
                            const inc = () => setCount(c => c + 1);
                            return { count, inc };
                            }

Les hooks custom encapsulent la logique de state.

3) Quand ne PAS utiliser useState
  • Donnée dérivable des props
  • Donnée globale (préférer context/store)
  • Donnée temporaire sans impact UI
Pièges courants & checklist
Pièges
  • Muter le state directement
  • Dupliquer la même donnée dans plusieurs states
  • setState en boucle infinie (via effects)
  • État trop profond (objets imbriqués complexes)
Checklist rapide
  • Le state est-il minimal ?
  • Immuable à chaque update ?
  • Bon niveau dans l’arbre ?
  • Fonctionnelle si dépendance à l’ancien state ?
80 % des bugs React débutants viennent d’un mauvais usage du state.
Phrase à retenir
“Si ton state est bien placé et immuable,
                    ton composant devient simple et prévisible.”
3.1 Lifecycle & useEffect — effets de bord, dépendances, cleanup & pièges réels
useEffect = gérer ce qui n’est PAS du rendu

Un composant React doit rester pur pendant le rendu (calcul JSX). Tout ce qui interagit avec “l’extérieur” (API, timers, DOM, subscriptions) est un effet de bord et doit vivre dans useEffect.

Exemples d’effets
  • Appels API / data fetching
  • WebSocket, EventSource, abonnements
  • setInterval, setTimeout
  • document.title, scroll, focus
Signature générale
useEffect(() => {
                            // 1) effet (après render)

                            return () => {
                            // 2) cleanup (optionnel)
                            };
                            }, [
                            // 3) dépendances
                            ]);
Règle clé : rendu = calcul, useEffect = effets.
Mental model
Render (JSX)
                    ↓
                    Commit DOM
                    ↓
                    useEffect s’exécute
[] — effet au montage (équivalent componentDidMount)

Avec un tableau de dépendances vide, l’effet s’exécute une seule fois, juste après le premier rendu.

useEffect(() => {
                    console.log("Mounted");
                    fetch("/api/init").then(...);
                    }, []);
Cas d’usage typiques
  • Chargement initial de données
  • Initialisation d’une lib externe
  • Abonnement global (avec cleanup)
Attention (React Strict Mode)

En mode développement, React peut exécuter l’effet deux fois pour détecter les effets non idempotents.

Un effet doit être idempotent (supporter plusieurs exécutions).
[deps] — effet à la mise à jour

Si tu fournis des dépendances, l’effet s’exécute : au montage puis à chaque changement d’une dépendance.

useEffect(() => {
                    console.log("userId changed:", userId);
                    fetch(`/api/users/${userId}`).then(...);
                    }, [userId]);
Que mettre dans les deps ?
  • Toutes les variables utilisées dans l’effet
  • Props et state référencés
  • Fonctions non stables (sauf memo)
Erreur fréquente
// ❌ dépendance manquante
                            useEffect(() => {
                            doSomething(value);
                            }, []); // value utilisé mais absent

Bug subtil : effet non synchronisé avec le state réel.

Cleanup — équivalent componentWillUnmount

Si ton effet crée un abonnement ou un processus long, tu dois nettoyer lors du démontage (ou avant ré-exécution).

useEffect(() => {
                    const id = setInterval(() => {
                    console.log("tick");
                    }, 1000);

                    return () => {
                    clearInterval(id);
                    };
                    }, []);
À nettoyer absolument
  • Timers (setInterval, setTimeout)
  • WebSocket / EventSource
  • Event listeners (addEventListener)
Règle senior
Si tu t’abonnes → tu te désabonnes.
Pas de cleanup = fuite mémoire ou comportements fantômes.
Data fetching avec useEffect

Le pattern classique : état loading, data, error. Attention aux race conditions.

useEffect(() => {
                    let cancelled = false;

                    setLoading(true);
                    fetch("/api/data")
                    .then(r => r.json())
                    .then(d => {
                    if (!cancelled) setData(d);
                    })
                    .finally(() => {
                    if (!cancelled) setLoading(false);
                    });

                    return () => {
                    cancelled = true;
                    };
                    }, []);

En pratique, beaucoup utilisent des libs dédiées (query cache), mais comprendre ce pattern est essentiel.

Dépendances & ESLint (exhaustive-deps)

La règle ESLint exhaustive-deps t’indique quelles dépendances manquent dans le tableau.

Bon réflexe
  • Corriger l’effet plutôt que désactiver la règle
  • Extraire la logique dans une fonction stable
  • Utiliser useCallback si nécessaire
❌ Mauvaise pratique
// eslint-disable-next-line react-hooks/exhaustive-deps
                            useEffect(() => {
                            doSomething(value);
                            }, []);

Acceptable uniquement si tu maîtrises parfaitement l’impact.

Pièges courants & checklist senior
Pièges
  • Mettre trop de logique dans un seul effect
  • Oublier le cleanup
  • Dépendances incomplètes
  • Effet qui modifie le state déclenchant lui-même
Checklist rapide
  • Est-ce vraiment un effet ?
  • Dépendances complètes et correctes ?
  • Cleanup présent si abonnement ?
  • Effet idempotent ?
Beaucoup de bugs React “avancés” viennent de useEffect mal maîtrisé.
Phrase à retenir
“Si ton rendu n’est pas pur,
                    ton useEffect est au mauvais endroit.”
3.2 Événements & Formulaires — onClick, onChange, Controlled Components
Gestion des événements en React

Les événements React (onClick, onChange, onSubmit, etc.) utilisent une syntaxe camelCase et reçoivent une fonction. On ne passe jamais une string ou un appel direct.

Handler classique
function AlertButton(){
                            const handleClick = (e) => {
                            e.preventDefault();
                            alert("Clic !");
                            };

                            return (
                            <button onClick={handleClick}>
                            Ne pas cliquer
                            </button>
                            );
                            }
Inline handler (si simple)
<button onClick={() => alert("Simple")}>
                            Clic simple
                            </button>

OK pour actions triviales. Sinon, préférer une fonction nommée.

Événements courants
  • onClick — interaction utilisateur
  • onChange — saisie input/select
  • onSubmit — envoi formulaire
  • onBlur / onFocus — focus
  • onKeyDown — clavier
SyntheticEvent : abstraction cross-browser

React encapsule les événements natifs dans un SyntheticEvent, garantissant un comportement cohérent entre navigateurs.

function Example(e){
                    console.log(e.type);        // click
                    console.log(e.target);     // élément DOM
                    console.log(e.currentTarget);
                    }
Aujourd’hui, les SyntheticEvents sont persistants (plus besoin de e.persist()).
Accès aux valeurs
onChange={(e) => {
                    const value = e.target.value;
                    }}
Controlled Components (principe fondamental)

En React, un formulaire est dit contrôlé lorsque la valeur affichée dans l’input est pilotée par le state. Le state devient la source de vérité.

Input contrôlé
function NameInput(){
                            const [name, setName] = React.useState("");

                            return (
                            <input
                            value={name}
                            onChange={(e) => setName(e.target.value)}
                            placeholder="Votre nom"
                            />
                            );
                            }
Pourquoi c’est puissant
  • Validation instantanée
  • Formatage automatique
  • Synchronisation UI / data
Règle : si l’input influence l’UI ou la logique → controlled.
Formulaire complet (submit, validation)
function LoginForm(){
                    const [email, setEmail] = React.useState("");
                    const [password, setPassword] = React.useState("");

                    const handleSubmit = (e) => {
                    e.preventDefault();
                    console.log({ email, password });
                    };

                    return (
                    <form onSubmit={handleSubmit}>
                    <input
                    type="email"
                    value={email}
                    onChange={(e) => setEmail(e.target.value)}
                    />
                    <input
                    type="password"
                    value={password}
                    onChange={(e) => setPassword(e.target.value)}
                    />
                    <button type="submit">Login</button>
                    </form>
                    );
                    }
Plusieurs champs (pattern)
const [form, setForm] = useState({ email: "", pwd: "" });

                    onChange={(e) =>
                    setForm(f => ({ ...f, [e.target.name]: e.target.value }))
                    }
Patterns “Senior”
1) Un state par formulaire
  • Évite la multiplication de useState
  • Facilite reset / validation
2) Validation dérivée
const isValid = email.includes("@") && password.length > 8;
3) Soumission async
const handleSubmit = async (e) => {
                            e.preventDefault();
                            setLoading(true);
                            await api.login(form);
                            setLoading(false);
                            };
Séparer logique (state) et UI (inputs) simplifie les tests.
Anti-patterns & pièges
❌ Non contrôlé sans raison
<input defaultValue="test" />

OK uniquement pour formulaires très simples / legacy.

❌ Handler lourd inline
<button onClick={() => doA(); doB(); doC();}>

Préférer une fonction dédiée.

Autres pièges
  • Oublier preventDefault() sur submit
  • Mettre trop de logique dans JSX
  • Pas de label → accessibilité cassée
Checklist UX & qualité
PointQuestionRed flag
ContrôleLe state est-il la source de vérité ?Inputs désynchronisés
UXFeedback loading / erreur ?Formulaire silencieux
AccessibilitéLabel + id associés ?Inputs non labellisés
LisibilitéHandlers nommés clairement ?Fonctions inline complexes
Phrase à retenir
“En React, un formulaire propre est
                    contrôlé, prévisible et accessible.”
3.3 Rendu React — Conditions, Listes (.map), key, Empty states & patterns “pro”
Rendu conditionnel : 4 patterns propres

En JSX, on ne met pas un if/else “brut” directement dans le markup. On utilise des expressions : early return, ternaire, &&, ou on prépare une variable.

1) Early return (souvent le plus clair)
function Page(){
                            if (loading) return <Spinner />;
                            if (error) return <ErrorBox />;
                            return <Dashboard />;
                            }
2) Ternaire (if/else inline)
<h1>{user ? `Bonjour, ${user.name}` : "Bonjour, visiteur"}</h1>
3) AND (if… alors)
{isAdmin && <AdminBadge />}
⚠️ Attention au “0”
// ❌ 0 s'affiche si items.length = 0
                            {items.length && <p>Items</p>}

                            // ✅ mieux
                            {items.length > 0 && <p>Items</p>}
4) Préparer une variable
let content = <Empty />;
                            if (items.length > 0) content = <List items={items} />;
                            return <div>{content}</div>;
Règle senior : si le JSX devient illisible (ternaires imbriqués), reviens à early return ou extrais en composant.
Listes : .map() = transformer des données en UI

On rend une liste en transformant un tableau de données en tableau d’éléments JSX. Le JSX accepte directement un tableau d’éléments.

Exemple simple
function TodoList({ todos }){
                            return (
                            <ul>
                            {todos.map(t => (
                            <li key={t.id}>{t.text}</li>
                            ))}
                            </ul>
                            );
                            }
Return implicite (style concis)
{todos.map(t => (
                            <TodoRow key={t.id} todo={t} />
                            ))}
Filtrer + mapper (pattern courant)
{todos
                            .filter(t => t.done)
                            .map(t => (
                            <li key={t.id}>{t.text}</li>
                            ))}
Rendu “empty” propre
{todos.length === 0
                            ? <EmptyState title="Aucune tâche" />
                            : <TodoList todos={todos} />}
Conseil pro : pour les listes longues (500+), pense à la virtualisation (sinon DOM lourd).
key : crucial pour le diffing (VDOM / réconciliation)

React doit identifier chaque item pour savoir s’il a été ajouté, supprimé ou déplacé. La prop key est l’identifiant unique et stable pour chaque élément.

✅ Bonne key
<li key={todo.id}>...</li>
  • Stable : ne change pas si la liste est triée/filtrée
  • Unique : pas de duplication
  • Idéal : ID base de données / UUID / slug
❌ Anti-pattern : key=index
{todos.map((t, i) => (
                            <TodoRow key={i} todo={t} />
                            ))}

Ne pas utiliser l’index si la liste peut bouger (tri, suppression au milieu, insertion).

Symptômes : inputs qui changent de valeur, focus qui saute, animations incohérentes, state mélangé entre lignes.

Key et “remount” volontaire

Parfois, tu veux forcer le remount d’un composant (reset state interne) en changeant sa key.

<UserForm key={userId} userId={userId} /> // change userId => reset du form
Empty / Loading / Error : le triptyque UX

Une UI “pro” gère toujours les 3 états : chargement, erreur, vide. Sinon l’app semble “cassée” ou silencieuse.

function UsersPanel(){
                    const { data, loading, error } = useUsers();

                    if (loading) return <SkeletonRows />;
                    if (error) return <ErrorBox message="Impossible de charger" />;
                    if (!data || data.length === 0) return <EmptyState title="Aucun utilisateur" />;

                    return <UsersTable users={data} />;
                    }
Loading UX
  • Skeletons > spinner (perception de vitesse)
  • Conserver layout pour éviter “layout shift”
Error UX
  • Message clair + action (Retry)
  • Logs / monitoring (en prod)
Patterns senior : rendre le JSX lisible
1) Render helper (fonction locale)
function Panel({ items, loading }){
                            const renderBody = () => {
                            if (loading) return <Spinner />;
                            if (items.length === 0) return <Empty />;
                            return <List items={items} />;
                            };

                            return <section>{renderBody()}</section>;
                            }
2) Extraire en composants
return (
                            <section>
                            <Header />
                            <ListOrEmpty items={items} />
                            </section>
                            );

Si ton composant mélange trop de branches de rendu, extraire = meilleur refactor “ROI”.

3) Branching UI par “state machine” (simple)
const status = loading ? "loading" : error ? "error" : items.length ? "ok" : "empty";

                    return (
                    <>
                    {status === "loading" && <Skeleton />}
                    {status === "error" && <ErrorBox />}
                    {status === "empty" && <Empty />}
                    {status === "ok" && <List items={items} />}
                    </>
                    );
Pièges réels (et comment les éviter)
1) Ternaires imbriqués
// ❌ illisible
                            {a ? (b ? <X/> : <Y/>) : (c ? <Z/> : <W/>)}

✅ Solution : early return / variable / composants.

2) Keys instables
// ❌ remount à chaque render
                            <Row key={Math.random()} />
3) map sans return (piège JS)
// ❌ { } nécessite un return explicite
                            {items.map(i => {
                            <li key={i.id}>{i.name}</li>
                            })}

                            // ✅
                            {items.map(i => (
                            <li key={i.id}>{i.name}</li>
                            ))}
4) Rendu de “falsy” inattendu
// "" rend rien (OK) mais 0 s'affiche
                            {value && <Tag />}
Si tu vois des bugs de focus / inputs “qui se mélangent” dans une liste : pense key en premier.
Checklist qualité (code review)
PointQuestionRed flag
ConditionsLe JSX reste lisible ?Ternaires imbriqués
ListesLa liste a-t-elle une key stable ?key=index / random
UXEmpty/loading/error gérés ?Écran vide “sans explication”
PerfListes longues optimisées ?1000+ nodes DOM sans virtualisation
Bug classFocus stable / inputs OK ?State mélangé entre items
Phrase à retenir
“Rendu lisible + keys stables + états UX gérés
                    = UI React robuste.”
4.1 Data Fetching React — fetch, Axios, React Query (TanStack)
Data fetching “vanilla” avec useEffect

Le pattern de base combine : useState (data / loading / error) + useEffect (déclenchement). Simple, mais vite répétitif et fragile sur des apps réelles.

function Articles(){
                    const [data, setData] = React.useState([]);
                    const [loading, setLoading] = React.useState(true);
                    const [error, setError] = React.useState(null);

                    React.useEffect(() => {
                    let cancelled = false;

                    const fetchData = async () => {
                    try {
                    const res = await fetch("/api/articles");
                    if (!res.ok) throw new Error("API error");
                    const json = await res.json();
                    if (!cancelled) setData(json);
                    } catch (e) {
                    if (!cancelled) setError(e.message);
                    } finally {
                    if (!cancelled) setLoading(false);
                    }
                    };

                    fetchData();
                    return () => { cancelled = true; };
                    }, []);

                    if (loading) return <Skeleton />;
                    if (error) return <ErrorBox message={error} />;
                    return <ArticleList items={data} />;
                    }
Ce pattern est pédagogique mais devient vite lourd à maintenir à grande échelle.
Axios : client HTTP plus ergonomique

Axios simplifie la gestion des headers, du JSON, des erreurs et permet des interceptors (auth, refresh token).

Client centralisé
// api/client.ts
                            import axios from "axios";

                            export const api = axios.create({
                            baseURL: "/api",
                            timeout: 5000,
                            });

                            api.interceptors.response.use(
                            res => res,
                            err => Promise.reject(err.response?.data || err)
                            );
Utilisation
const { data } = await api.get("/articles");
                            setArticles(data);

Très utile pour auth JWT, headers globaux, retry custom.

Problèmes classiques du data fetching “manuel”
  • Duplication de code (loading/error partout)
  • Race conditions (réponses hors ordre)
  • Pas de cache → refetch inutile
  • Gestion complexe des refresh / refocus
  • Synchronisation inter-composants difficile
// Exemple de bug fréquent
                    useEffect(() => {
                    fetch(`/api/users/${id}`).then(setUser);
                    }, [id]); // id change vite → réponses mélangées
Ces problèmes ont motivé l’émergence de libs spécialisées.
React Query (TanStack Query) — le standard moderne

React Query gère le serveur-state : cache, refetch, loading, error, synchronisation automatique.

Query simple
import { useQuery } from "@tanstack/react-query";

                            const fetchArticles = async () => {
                            const res = await fetch("/api/articles");
                            if (!res.ok) throw new Error("API error");
                            return res.json();
                            };

                            function Articles(){
                            const { data, isLoading, error } = useQuery({
                            queryKey: ["articles"],
                            queryFn: fetchArticles,
                            });

                            if (isLoading) return <Skeleton />;
                            if (error) return <ErrorBox />;
                            return <ArticleList items={data} />;
                            }
Ce que React Query apporte
  • Cache automatique
  • Refetch au focus / reconnect
  • Déduplication des requêtes
  • Pagination & infinite queries
Aujourd’hui, c’est le choix par défaut en React pro.
Mutations (POST / PUT / DELETE)

Les mutations modifient le serveur-state. React Query gère loading, error et invalidation du cache.

import { useMutation, useQueryClient } from "@tanstack/react-query";

                    function AddArticle(){
                    const qc = useQueryClient();

                    const mutation = useMutation({
                    mutationFn: (data) =>
                    fetch("/api/articles", {
                    method: "POST",
                    body: JSON.stringify(data),
                    }),
                    onSuccess: () => {
                    qc.invalidateQueries(["articles"]);
                    },
                    });

                    return (
                    <button onClick={() => mutation.mutate({ title: "New" })}>
                    Add
                    </button>
                    );
                    }

Invalidation = synchronisation automatique de toute l’app.

Architecture “Senior” recommandée
Organisation
  • services/api.ts (clients HTTP)
  • queries/*.ts (hooks React Query)
  • Composants = UI uniquement
Exemple hook dédié
// queries/useArticles.ts
                            export const useArticles = () =>
                            useQuery({
                            queryKey: ["articles"],
                            queryFn: fetchArticles,
                            });
Séparer UI / data fetching = composants plus simples et testables.
Checklist & choix technologiques
BesoinSolution
Prototype simpleuseEffect + fetch
Auth / headers complexesAxios
App pro / scalableReact Query
Cache & sync multi-composantsReact Query
Phrase à retenir
“Le state serveur n’est pas du state local :
                    traite-le comme une ressource partagée.”
4.2 Routage — React Router (v6+) : Routes, Layout, Link, Params, Guards
React n’inclut pas de routeur

Pour une SPA, la librairie standard est React Router (DOM). Elle mappe une URL vers un composant “page” sans recharger la page.

npm install react-router-dom
main.jsx — setup minimal

On “enveloppe” l’app avec <BrowserRouter>.

import React from "react";
                    import ReactDOM from "react-dom/client";
                    import { BrowserRouter } from "react-router-dom";
                    import App from "./App";

                    ReactDOM.createRoot(document.getElementById("root")).render(
                    <React.StrictMode>
                    <BrowserRouter>
                    <App />
                    </BrowserRouter>
                    </React.StrictMode>
                    );
BrowserRouter = history API (URLs “propres”). Alternative: HashRouter (fallback si serveur ne gère pas le refresh).
Routes v6 : Routes, Route, Layout & Outlet

Le pattern “pro” : un composant Layout (Navbar + footer) qui encadre les pages via <Outlet />.

import { Routes, Route } from "react-router-dom";
                    import Layout from "./components/Layout";
                    import HomePage from "./pages/HomePage";
                    import AboutPage from "./pages/AboutPage";
                    import UserProfile from "./pages/UserProfile";
                    import NotFound from "./pages/NotFound";

                    export default function App(){
                    return (
                    <Routes>
                    {/* Layout partagé */}
                    <Route path="/" element={<Layout />}>

                    {/* index = page "/" */}
                    <Route index element={<HomePage />} />

                    {/* pages simples */}
                    <Route path="about" element={<AboutPage />} />

                    {/* route dynamique */}
                    <Route path="users/:userId" element={<UserProfile />} />

                    {/* 404 */}
                    <Route path="*" element={<NotFound />} />
                    </Route>
                    </Routes>
                    );
                    }
Layout.jsx — Outlet
import { Outlet } from "react-router-dom";
                    import Navbar from "./Navbar";

                    export default function Layout(){
                    return (
                    <div className="app-shell">
                    <Navbar />
                    <main className="container">
                    <Outlet />
                    </main>
                    <footer>© IDEO-Lab</footer>
                    </div>
                    );
                    }
Les routes imbriquées évitent de répéter la navbar, la structure et les wrappers sur chaque page.
Navigation interne : Link et NavLink

Pour naviguer dans une SPA, on utilise <Link> (pas <a href>), afin d’éviter le rechargement complet.

Link
import { Link } from "react-router-dom";

                            <nav>
                            <Link to="/">Accueil</Link>
                            <Link to="/about">À propos</Link>
                            </nav>
NavLink (active state)
import { NavLink } from "react-router-dom";

                            <NavLink
                            to="/about"
                            className={({ isActive }) => isActive ? "nav-item active" : "nav-item"}
                            >
                            À propos
                            </NavLink>

Pratique pour mettre en surbrillance l’onglet actif dans un menu.

Relative links (dans routes imbriquées)
// depuis /users
                    <Link to="42">User 42</Link>     // -> /users/42
Routes dynamiques : :param + useParams

Une route dynamique capture des segments d’URL pour construire une page “détail”. Exemple: /users/42userId = 42.

import { useParams } from "react-router-dom";

                    export default function UserProfile(){
                    const { userId } = useParams();

                    // ex: useEffect(() => fetch(`/api/users/${userId}`), [userId])
                    return <h1>Profil de l'utilisateur {userId}</h1>;
                    }
useParams() renvoie des strings : si tu attends un nombre, parse/valide côté code.
Navigation “code” : useNavigate

Quand tu dois naviguer après une action (login, submit, save), utilise useNavigate().

import { useNavigate } from "react-router-dom";

                    function Login(){
                    const navigate = useNavigate();

                    const onSuccess = () => {
                    navigate("/dashboard");     // go to
                    // navigate(-1);            // back
                    // navigate("/users/42", { replace: true });
                    };

                    return <button onClick={onSuccess}>Login</button>;
                    }
Lire / écrire les query params
import { useSearchParams } from "react-router-dom";

                    function Products(){
                    const [params, setParams] = useSearchParams();
                    const q = params.get("q") || "";

                    return (
                    <input
                    value={q}
                    onChange={(e) => setParams({ q: e.target.value })}
                    />
                    );
                    }
404, redirections & guards (routes protégées)
404 (catch-all)
<Route path="*" element={<NotFound />} />
Redirection
import { Navigate } from "react-router-dom";

                            <Route path="old" element={<Navigate to="/new" replace />} />
Route protégée (auth guard)
import { Navigate, Outlet } from "react-router-dom";

                            function RequireAuth({ isAuthed }){
                            if (!isAuthed) return <Navigate to="/login" replace />;
                            return <Outlet />;
                            }

                            // Usage
                            <Route element={<RequireAuth isAuthed={isAuthed} />}>
                            <Route path="dashboard" element={<Dashboard />} />
                            </Route>
Pattern propre : un “guard” rend <Outlet /> si OK, sinon redirige.
Important (prod) : config serveur

En BrowserRouter, un refresh sur /about doit renvoyer index.html. Sinon : 404 côté serveur. Il faut donc une règle “fallback to index.html”.

Patterns senior & pièges
Bonnes pratiques
  • Routes par feature (pages regroupées par domaine).
  • Layout + routes imbriquées (Outlet) = structure claire.
  • Utiliser NavLink pour l’état “actif”.
  • Gérer 404 / loading / error proprement.
Code splitting (lazy)
import { lazy, Suspense } from "react";

                            const Settings = lazy(() => import("./pages/Settings"));

                            <Route
                            path="settings"
                            element={
                            <Suspense fallback={<Spinner />}>
                            <Settings />
                            </Suspense>
                            }
                            />
Pièges fréquents
  • <a href> au lieu de <Link> → reload complet.
  • Oublier le fallback serveur → 404 au refresh.
  • Routes trop plates (pas de Layout) → duplication de wrappers.
  • Paramètres non validés (IDs) → erreurs silencieuses.
Si ton app marche en navigation mais casse au refresh sur une route profonde : c’est (presque toujours) la config serveur.
Checklist express
PointOK ?Note
Layout + Outletstructure stable
404 catch-allUX propre
NavLink actifnavigation claire
Fallback serveurprod stable
4.3 State global — useContext : partage d’état, Provider & limites
Prop Drilling : le symptôme classique

Le prop drilling consiste à faire transiter une donnée à travers plusieurs composants intermédiaires qui n’en ont pas besoin, uniquement pour atteindre un composant lointain.

App (user)
                    └─ Layout (passe user)
                    └─ Sidebar (passe user)
                    └─ Page
                    └─ UserProfile (utilise user)
  • Composants pollués par des props inutiles
  • Couplage fort entre couches
  • Refactors douloureux
Si tu passes une prop sur 3+ niveaux sans l’utiliser : c’est un signal.
Context : un “tunnel” de données

Context permet de rendre une valeur disponible partout dans un sous-arbre, sans la passer explicitement en props.

createContext → Provider → useContext
Quand utiliser Context ?
  • Utilisateur authentifié
  • Thème (dark/light)
  • Langue / i18n
  • Permissions / rôles
Quand éviter ?
  • Données très locales
  • Données très volatiles (perf)
  • State complexe avec logique lourde
Mise en place pas à pas
1️⃣ Créer le contexte
import { createContext } from "react";

                    export const AuthContext = createContext(null);
2️⃣ Fournir la valeur (Provider)
import { AuthContext } from "./AuthContext";

                    function App(){
                    const user = { name: "Alice", role: "admin" };

                    return (
                    <AuthContext.Provider value={user}>
                    <Layout />
                    </AuthContext.Provider>
                    );
                    }
3️⃣ Consommer la valeur
import { useContext } from "react";
                    import { AuthContext } from "./AuthContext";

                    function UserProfile(){
                    const user = useContext(AuthContext);
                    return <h1>Bonjour {user.name}</h1>;
                    }
Les composants intermédiaires n’ont plus aucune connaissance de user.
Context + State (cas réel)

Très souvent, le contexte expose un state + des actions.

export const AuthContext = createContext(null);
                    
                    function AuthProvider({ children }){
                    const [user, setUser] = useState(null);

                    const login = (u) => setUser(u);
                    const logout = () => setUser(null);

                    return (
                    <AuthContext.Provider value={{ user, login, logout }}>
                    {children}
                    </AuthContext.Provider>
                    );
                    }
                    

                
// Usage
                    const { user, logout } = useContext(AuthContext);
Pattern standard : Provider dédié + value structurée.
Patterns senior
1) Custom hook
export const useAuth = () => {
                            const ctx = useContext(AuthContext);
                            if (!ctx) throw new Error("useAuth must be used in AuthProvider");
                            return ctx;
                            };

Simplifie l’import et sécurise l’usage.

2) Context découpés
  • AuthContext
  • ThemeContext
  • SettingsContext

Évite un “God Context” gigantesque.

3) Context + useReducer
const [state, dispatch] = useReducer(reducer, initialState);

Recommandé quand la logique devient plus complexe.

Limites & alternatives
BesoinSolution
Partager 1–2 valeurs simplesuseContext
State serveur (API)React Query
State global complexeRedux / Zustand / Jotai
Perf très critiqueStore spécialisé
Chaque update de Context déclenche un re-render de tous les consommateurs.
Checklist & pièges
Pièges
  • Mettre tout le state global dans un seul Context
  • Utiliser Context pour chaque petit state
  • Oublier le Provider → null
Checklist
  • Évite-t-il vraiment le prop drilling ?
  • Valeur stable / memo si besoin ?
  • Context limité à un domaine clair ?
Phrase à retenir
“Context est un excellent outil…
                    à condition de ne pas en faire un mini-Redux.”
4.4 State Global (Avancé) — Redux Toolkit & Zustand
Pourquoi dépasser useContext ?

useContext est parfait pour des données peu volatiles (thème, utilisateur connecté). Dès que le state devient fréquent, volumineux ou critique, il faut un store global optimisé.

Problèmes typiques
  • Re-renders globaux inutiles
  • Logique métier dispersée
  • Difficulté de debug
  • Synchronisation complexe
Quand un store global est justifié
  • Dashboard complexe
  • Workflow multi-pages
  • État partagé par de nombreux composants
  • Besoin de time-travel / debug
Règle simple : si le state est stratégique pour l’app → store global.
Redux Toolkit (RTK) — standard entreprise

Redux Toolkit est la version moderne et recommandée de Redux. Il réduit drastiquement le boilerplate historique tout en gardant une architecture prédictible et robuste.

UI → dispatch(action)
                    ↓
                    reducer (fonction pure)
                    ↓
                    store (state global)
                    ↓
                    useSelector → UI
Concepts clés
  • Store : source unique de vérité
  • Slice : domaine fonctionnel (user, cart, settings)
  • Reducer : logique de mise à jour
  • Action : intention (“user/login”)
RTK utilise Immer → écriture “mutable”, résultat immuable.
Redux Toolkit — exemple réaliste
Slice
import { createSlice } from "@reduxjs/toolkit";

                    const userSlice = createSlice({
                    name: "user",
                    initialState: { user: null },
                    reducers: {
                    login: (state, action) => {
                    state.user = action.payload;
                    },
                    logout: (state) => {
                    state.user = null;
                    },
                    },
                    });

                    export const { login, logout } = userSlice.actions;
                    export default userSlice.reducer;
Store
import { configureStore } from "@reduxjs/toolkit";
                    import userReducer from "./userSlice";

                    export const store = configureStore({
                    reducer: {
                    user: userReducer,
                    },
                    });
Utilisation dans un composant
import { useSelector, useDispatch } from "react-redux";
                    import { login } from "./userSlice";

                    function Login(){
                    const dispatch = useDispatch();
                    const user = useSelector(s => s.user.user);

                    return (
                    <button onClick={() => dispatch(login({ name: "Alice" }))}>
                    Login
                    </button>
                    );
                    }

Avantages : traçabilité, testabilité, DevTools puissants.

Zustand — minimalisme et efficacité

Zustand est un store global ultra-léger basé sur des hooks. Pas de Provider, pas de reducers, très peu de code.

import { create } from "zustand";

                    export const useStore = create((set) => ({
                    count: 0,
                    inc: () => set(s => ({ count: s.count + 1 })),
                    reset: () => set({ count: 0 }),
                    }));
Consommation
function Counter(){
                    const count = useStore(s => s.count);
                    const inc = useStore(s => s.inc);

                    return <button onClick={inc}>{count}</button>;
                    }
Sélecteurs fins → re-render uniquement si la valeur change.
Redux Toolkit vs Zustand
CritèreRedux ToolkitZustand
BoilerplateMoyenTrès faible
Scalabilité⭐⭐⭐⭐⭐⭐⭐⭐
Debug / DevToolsExcellentBasique
Courbe d’apprentissagePlus raideTrès douce
Cas idéalApp entrepriseApp moderne / rapide
Patterns senior
Redux
  • Un slice par domaine métier
  • Sélecteurs mémorisés
  • Async via RTK Query
Zustand
  • Store par feature
  • State + actions ensemble
  • Pas de “God store”
Mélanger React Query (server state) + Redux/Zustand (client state) est une excellente pratique.
Pièges & checklist
Pièges
  • Tout mettre dans le store global
  • Dupliquer le server state
  • Reducers trop génériques
Checklist
  • Le state est-il vraiment global ?
  • Server state séparé ?
  • Sélecteurs précis ?
Phrase à retenir
“Un bon store global rend l’app prévisible,
                    pas compliquée.”
5.1 Build & Déploiement — Vite/React : build, CI/CD, Vercel/Netlify, Nginx, Docker
React = build-time : dev server ≠ production

Le serveur de dev (npm run dev) sert à itérer vite (HMR). En production, on déploie un bundle statique généré par npm run build (Vite) dans dist/.

# Build production
                    npm run build

                    # Résultat (Vite)
                    dist/
                    index.html
                    assets/
                    index-xxxx.js
                    index-yyyy.css
Ce que fait le build
  • Minification JS/CSS + tree-shaking
  • Bundling + hashing des assets (cache busting)
  • Optimisation des imports, chunks, sourcemaps (option)
Ce que tu déploies
  • Uniquement dist/ (ou le contenu servi)
  • Pas node_modules
  • Pas le dev server
Pour une SPA “classique” : React = site statique + JS.
Preview local “comme en prod”

Toujours tester le bundle prod localement avant de pousser en prod. Avec Vite : npm run preview sert le dossier dist/.

npm run build
                    npm run preview
                    # ouvre http://localhost:4173 (souvent)
Checklist QA
  • Routage (refresh sur /route)
  • 404 (page not found) & redirections
  • Console propre (pas d’erreurs)
  • Env vars (API URL) correctes
Conseil “pro”
  • Build + lint + tests en CI
  • Build “reproductible” (lockfile)
  • Preview deploy (PR previews sur Vercel/Netlify)
Variables d’environnement (Vite) : import.meta.env

En Vite, les variables exposées au frontend doivent être préfixées par VITE_. Elles sont injectées au build (important).

# .env.production
                    VITE_API_BASE_URL=https://api.monsite.com
// usage
                    const API = import.meta.env.VITE_API_BASE_URL;
Une variable d’env frontend n’est pas secrète. Ne jamais mettre de clé privée dans .env côté client.
Base path (si app servie sous un sous-répertoire)
// vite.config.js
                    export default defineConfig({
                    base: "/react/", // exemple si app servie sur https://site.com/react/
                    });
Déploiement statique (Jamstack) : Vercel / Netlify

Le plus simple pour une SPA : pousser sur GitHub/GitLab, connecter Vercel/Netlify, et laisser le CI/CD faire.

Configuration typique
  • Build command : npm run build
  • Output directory : dist
  • Node version : fixée (ex: 18/20) via settings
  • Env vars : dans le dashboard (prod/staging)
SPA routing (très important)

React Router gère les routes côté client. Il faut une règle “fallback to index.html”.

# Netlify: _redirects (dans /public ou dist selon setup)
                            /*  /index.html  200
# Vercel (concept)
                            toute route -> index.html (SPA)
Bonus : preview deploy par pull request
  • Chaque PR ⇒ URL de preview (QA rapide)
  • Merge main ⇒ prod auto
Déploiement Nginx (VPS / bare-metal)

Tu buildes en CI (ou local), tu copies dist/ sur le serveur, et Nginx sert les fichiers. Le point critique : fallback SPA (sinon 404 au refresh).

# Copier dist/ sur le serveur
                    rsync -avz dist/ user@server:/var/www/react-app/
Nginx config (SPA + cache + fallback)
# /etc/nginx/sites-available/react-app
                    server {
                    listen 80;
                    server_name example.com;

                    root /var/www/react-app;
                    index index.html;

                    # assets hashés : cache long
                    location /assets/ {
                    try_files $uri =404;
                    expires 1y;
                    add_header Cache-Control "public, immutable";
                    }

                    # SPA fallback
                    location / {
                    try_files $uri $uri/ /index.html;
                    }
                    }
try_files ... /index.html = “Front Controller” SPA (indispensable avec React Router).
HTTPS (pro)
  • Certbot / Let’s Encrypt (ou reverse proxy existant)
  • Redirect HTTP → HTTPS
Docker (pro) : build multi-stage + Nginx

Pattern standard : builder Node ⇒ artefacts statiques ⇒ image Nginx légère.

# Dockerfile (multi-stage)
                    FROM node:20-alpine AS build
                    WORKDIR /app
                    COPY package*.json ./
                    RUN npm ci
                    COPY . .
                    RUN npm run build

                    FROM nginx:alpine
                    COPY --from=build /app/dist /usr/share/nginx/html
                    # SPA fallback custom (option)
                    # COPY nginx.conf /etc/nginx/conf.d/default.conf
                    EXPOSE 80
Run
docker build -t react-app .
                            docker run -p 8080:80 react-app
Pourquoi Docker ici ?
  • Build reproductible
  • Déploiement simple (K8s, Swarm, ECS)
  • Rollback facile
Perf, cache, sécurité : ce qui fait la différence en prod
Perf & cache
  • Assets hashés : cache long (immutable)
  • HTML : cache court (ou no-cache)
  • Compression : gzip/brotli (selon infra)
  • Code splitting (lazy routes)
  • Images optimisées + lazy loading
Sourcemaps
  • Utile pour debugging (Sentry, etc.)
  • À contrôler selon politique sécurité
Sécurité (headers)
  • CSP (Content-Security-Policy) si possible
  • HSTS en HTTPS
  • X-Content-Type-Options nosniff
  • Referrer-Policy
Un frontend React ne doit jamais contenir de secrets (API keys privées, tokens).
Checklist “release”
ItemOK ?Notes
npm run build cleanpas d’erreurs/warnings critiques
npm run preview validérouting + env
Fallback SPA en prodNginx / Netlify / Vercel
Cache assets hashésimmutable
Monitoring erreursSentry / logs
Phrase à retenir
“Build statique + routing SPA + cache correct
                    = déploiement React robuste.”
6.1 Cheat-sheet React — Hooks essentiels & patterns rapides
🧠 Hooks fondamentaux
1️⃣ useState — état local
const [count, setCount] = useState(0);

                        // update simple
                        setCount(1);

                        // update basé sur l'ancien state (recommandé)
                        setCount(c => c + 1);

                        // array / object (immutabilité)
                        setItems(prev => [...prev, newItem]);
                        setUser(prev => ({ ...prev, age: prev.age + 1 }));
2️⃣ useEffect — effets & lifecycle
// mount (1 fois)
                        useEffect(() => {
                        fetchData();
                        }, []);

                        // dépendance
                        useEffect(() => {
                        fetch(`/api/users/${id}`);
                        }, [id]);

                        // cleanup
                        useEffect(() => {
                        const id = setInterval(tick, 1000);
                        return () => clearInterval(id);
                        }, []);
3️⃣ useContext — state partagé
const ThemeContext = createContext("light");

                        function App(){
                        return (
                        <ThemeContext.Provider value="dark">
                        <Page />
                        </ThemeContext.Provider>
                        );
                        }

                        const theme = useContext(ThemeContext);
💡 Astuce : toujours encapsuler dans un custom hook (useTheme())
⚙️ Hooks avancés & perf
4️⃣ useRef — DOM & valeur persistante
// DOM
                        const inputRef = useRef(null);
                        <input ref={inputRef} />
                        inputRef.current.focus();

                        // valeur persistante (sans re-render)
                        const renderCount = useRef(0);
                        renderCount.current++;
5️⃣ useMemo — mémoïsation valeur
const total = useMemo(() => {
                        return items.reduce((a, b) => a + b.price, 0);
                        }, [items]);
6️⃣ useCallback — mémoïsation fonction
const handleClick = useCallback(() => {
                        doSomething(id);
                        }, [id]);
⚠️ Ne pas sur-optimiser : useMemo/useCallback seulement si utile
📋 Récap express (quand utiliser quoi)
HookRôleÀ utiliser quand…À éviter si…
useStateÉtat localUI interactiveDonnée dérivable
useEffectEffets de bordAPI, timers, subscriptionsLogique de rendu
useContextState partagéAuth, thème, settingsState très fréquent
useRefRéférence mutableDOM, valeur persistanteUI réactive
useMemoPerf (valeur)Calcul coûteuxCalcul trivial
useCallbackPerf (fonction)Props enfants memoHandlers simples
⌨️ CLI rapide (Vite + React)
# créer projet
                npm create vite@latest my-app
                cd my-app

                # installer deps
                npm install

                # dev
                npm run dev

                # build prod
                npm run build

                # preview prod
                npm run preview

                # deps courantes
                npm install react-router-dom
                npm install @tanstack/react-query
                npm install zustand
🧩 Phrase à retenir
“Si tu maîtrises useState, useEffect et useContext,
                tu comprends déjà 80 % de React.”