React 129 termes

129 termes affichés

TypeBestPractice Concept Method Pattern Principle Security Tool
Niveau 🟢 Junior 🟡 Mid 🔴 Senior

Mis à jour ces 90 derniers jours 30 nouvelles · 48 révisées

'use cache' et Cache Components (Next.js)<Activity><ViewTransition> et addTransitionTypeActions (transitions asynchrones)API de préchargement (preload, preinit, preconnect, prefetchDNS)browser() — rendu navigateur uniquementcache() et cacheSignalcacheLife, cacheTag, updateTag, refreshCVE-2025-55182 (RCE dans les Server Components)Document Metadata et feuilles de style nativeseslint-plugin-react-hooks v6 (Rules of React)Fin de Create React AppFragment RefsInstant Navigations et Partial Prefetching (Next.js 16.3)Middleware (React Router v8)Owner Stack (captureOwnerStack)prerender() / resume() (react-dom/static)proxy.ts (ex-middleware.ts)React CompilerReact Performance Tracks (Chrome DevTools)React Router — Framework Moderef as a prop (fin de forwardRef)RSC Payload (sérialisation serveur → client)Server Functions ('use server')Suppressions de React 19 (propTypes, defaultProps, ReactDOM.render)TanStack StartTurbopackuseActionStateuseEffectEventVite + @vitejs/plugin-react'use server'Action (React Router)Automatic BatchingCode SplittingComponent LifecycleCompound ComponentsConcurrent RenderingContext APICustom HookError BoundaryHOC (Higher-Order Component)ISR (Incremental Static Regeneration)JSXLoader (React Router)Nested RoutesNext.js App RouterNext.js MiddlewareOutletPartial PrerenderingPropsProvider PatternReact Hook FormReact Router v6React Server ComponentsReact Testing LibraryReact.memoReconciliation AlgorithmRedux ToolkitRemix ConventionsRender PropsServer ActionsSSG (Static Site Generation)SSR (Server-Side Rendering)StateStreaming SSRStrictModeSuspenseTanStack Queryuse() HookuseCallbackuseContextuseImperativeHandleuseMemouseOptimisticuseReduceruseTransitionVirtualizationZustand

Directive Next.js (expérimentale en 15, activée par cacheComponents: true depuis Next.js 16, 21 octobre 2025) qui rend cachable une page, un composant ou une fonction async. La clé de cache est dérivée automatiquement par le compilateur (ID de build, ID de fonction, arguments et variables capturées). Tout ce qui n'est pas marqué est dynamique par défaut : le cache implicite de l'ancien App Router disparaît. Les portées 'use cache' constituent la coquille statique du Partial Prerendering.

Un plat cuisiné sous vide : préparé une fois, réchauffé à chaque service tant que la date (cacheLife) n'est pas dépassée.

// next.config.ts
export default { cacheComponents: true };

// lib/data.ts
import { cacheLife, cacheTag } from 'next/cache';
export async function getProducts(category: string) {
  'use cache';
  cacheLife('hours');            // durée explicite recommandée
  cacheTag('products');          // invalidation ciblée
  return db.products.findMany({ where: { category } });
}
// Composant entier cachable (doit être async)
export async function Footer() { 'use cache'; return <footer>…</footer>; }

Cas d'usage : Mettre en cache des requêtes BDD ou des blocs de page coûteux tout en gardant le reste dynamique, avec navigation instantanée côté client.

Anti-pattern : Lire cookies(), headers() ou searchParams dans une portée 'use cache' (erreur next-request-in-use-cache) ; passer une instance de classe en argument (non sérialisable).
#nextjs#caching#performance#interview

'use client'

React 🟡 Mid

Directive qui marque un composant comme Client Component, activant l'interactivité (state, effets, handlers). Frontière entre serveur et client dans l'arbre.

Comme le panneau 'zone libre-service' dans un magasin : a partir de la, c'est le client qui interagit.

'use client';

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

Cas d'usage : Composants interactifs : formulaires, boutons, modales, tout ce qui a besoin de state ou d'event handlers.

Anti-pattern : Mettre 'use client' a la racine de l'app, perdant tous les avantages des Server Components.
#rendering#react19#nextjs

'use server'

React 🟡 Mid

Directive qui marque une fonction (ou tout un module) comme Server Function : elle s'exécute sur le serveur mais le client en reçoit une référence appelable, comme un RPC. Elle ne marque PAS un Server Component : les composants serveur sont le défaut, sans directive.

Comme un bouton dans un ascenseur : tu appuies cote client, mais le moteur (serveur) fait tout le travail.

'use server';

export async function createUser(formData) {
  const name = formData.get('name');
  await db.insert({ name });
  revalidatePath('/users');
}

Cas d'usage : Mutations de données (formulaires, CRUD) sans créer de route API séparée.

Anti-pattern : Croire que 'use server' protège la fonction : elle crée un endpoint public. Arguments et retour doivent être sérialisables ; la désérialisation de ces requêtes a été le vecteur de la CVE-2025-55182 (3 décembre 2025), d'où l'importance de tenir react-server-dom-* à jour.
#rendering#react19#nextjs#security

<Activity>

React 🟡 Mid

Composant livré dans React 19.2 (1er octobre 2025) qui masque ou révèle un sous-arbre sans le démonter. En mode hidden, le contenu est caché en display: none, l'état React et le DOM (brouillon de textarea, position vidéo) sont conservés, les effets sont nettoyés et le rendu continue à basse priorité. Il permet aussi de pré-rendre en arrière-plan un écran pas encore visible.

Mettre une pièce en veille : lumières éteintes, mais les meubles restent en place pour le retour.

import { Activity } from 'react';

function App({ tab }) {
  return (
    <>
      <Activity mode={tab === 'home' ? 'visible' : 'hidden'}>
        <Home />     {/* état conservé quand on change d'onglet */}
      </Activity>
      <Activity mode={tab === 'posts' ? 'visible' : 'hidden'}>
        <Posts />    {/* pré-rendu à basse priorité si caché */}
      </Activity>
    </>
  );
}

Cas d'usage : Onglets, barres latérâles, écrans d'un flux multi-étapes dont on veut garder l'état ; pré-chargement du code et des données d'une page probable.

Anti-pattern : Cacher une <vidéo> ou un <iframe> sans couper la lecture dans un nettoyage d'effet : les effets de bord du DOM persistent.
#core#react19#performance

Composant stable depuis React 19.3 (9 septembre 2026), après une phase expérimentale annoncée le 23 avril 2025. Il anime les éléments qui entrent, sortent, se déplacent ou se partagent entre écrans en s'appuyant sur l'API View Transition du navigateur. Il ne se déclenche que dans une transition (startTransition, Suspense, useDeferredValue). addTransitionType permet de choisir l'animation selon la cause (navigation avant/arrière).

Le fondu enchaîné d'un monteur vidéo, appliqué automatiquement entre deux plans de votre interface.

import { ViewTransition, addTransitionType, startTransition } from 'react';

<ViewTransition
  enter={{ 'nav-forward': 'slide-left', 'nav-back': 'slide-right', default: 'auto' }}
  exit="auto"
>
  <Page />
</ViewTransition>

// Dans le routeur
startTransition(() => {
  addTransitionType('nav-' + direction);
  navigate(url);
});
/* CSS : ::view-transition-old(.slide-left) { animation: ... } */

Cas d'usage : Transitions de pages, agrandissement d'une vignette en plein écran (prop name partagée), apparition/disparition d'items de liste.

Anti-pattern : Placer <ViewTransition> sous un nœud DOM (il doit englober directement le contenu animé) ou ignorer prefers-reduced-motion.
#ui#react19#animation

Action (React Router)

React 🟡 Mid

Fonction associée à une route qui traite les mutations (POST, PUT, DELETE) soumises par un <Form>. Après l'action, React Router revalide automatiquement les loaders de la page. Depuis la v8 (17 juin 2026), l'action peut être précédée d'un middleware (auth, contexte), activé par défaut.

Comme la boite aux lettres d'un bureau : tu deposes ton courrier (formulaire), le bureau (action) le traite.

import { redirect } from 'react-router';
import type { Route } from './+types/new-user';

export async function action({ request }: Route.ActionArgs) {
  const form = await request.formData();
  await api.createUser(Object.fromEntries(form));
  return redirect('/users');   // les loaders sont revalidés
}
// <Form method="post"> déclenche l'action sans JS custom

Cas d'usage : Gérer les soumissions de formulaires avec revalidation automatique des loaders après mutation.

#routing#forms

Actions (transitions asynchrones)

React 🟡 Mid? entretien

Depuis React 19 (5 décembre 2024), une fonction asynchrone passée à startTransition ou à la prop action d'un <form> est une « Action ». React gère pour vous l'état pending, le séquencement, les erreurs (remontées à l'Error Boundary) et la réinitialisation du formulaire. Les hooks useActionState, useFormStatus et useOptimistic sont bâtis dessus.

Un serveur de restaurant qui prend la commande, annonce « c'est en cours », et revient vous prévenir si la cuisine a un souci : vous n'avez plus à surveiller la porte.

function UpdateName() {
  const [name, setName] = useState('');
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  function handleSubmit() {
    startTransition(async () => {         // Action
      const err = await updateName(name); // isPending géré par React
      if (err) { setError(err); return; }
      redirect('/profile');
    });
  }
  return <button onClick={handleSubmit} disabled={isPending}>Enregistrer</button>;
}

Cas d'usage : Toute mutation déclenchée par l'utilisateur (enregistrer, liker, supprimer) où l'on affichait auparavant un état isLoading manuel.

Anti-pattern : Gérer à la main isLoading/error avec trois useState alors qu'une Action fournit isPending et la propagation d'erreur.
#core#react19#interview

Fonctions de react-dom stables depuis React 19 (5 décembre 2024) qui indiquent au navigateur les ressources à venir : prefetchDNS(url), preconnect(url), preload(url, { as }) et preinit(url, { as }) (télécharge ET exécute un script ou applique une feuille de style). En SSR, React émet les balises correspondantes dans le <head> ; côté client, elles s'ajoutent dynamiquement.

Réserver la table et commander l'entrée avant d'arriver au restaurant : quand vous vous asseyez, tout est déjà en route.

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function ProductPage() {
  preinit('/scripts/checkout.js', { as: 'script' });   // charge + exécute
  preload('/fonts/inter.woff2', { as: 'font' });       // charge seulement
  preconnect('https://cdn.images.example');            // handshake TLS
  prefetchDNS('https://analytics.example');
  return <Gallery />;
}

Cas d'usage : Réduire la latence perçue d'une interaction probable (paiement, galerie) en anticipant les ressources dès le rendu du composant.

Anti-pattern : Précharger des dizaines de ressources « au cas où » : on sature la bande passante du chargement initial.
#performance#react19

Automatic Batching

React 🟡 Mid

Depuis React 18, React regroupe automatiquement plusieurs mises à jour d'état en un seul re-rendu, y compris dans les callbacks asynchrones, les promesses et les timeouts. Ce comportement est acquis : la version stable actuelle est React 19.3 (9 septembre 2026). React 19.3 étend même le batching aux événements resize.

Comme un facteur qui attend d'avoir tout le courrier du quartier avant de faire sa tournée, au lieu d'un aller-retour par lettre.

// React 18: un seul re-rendu pour les deux
async function handleClick() {
  const data = await fetchData();
  setName(data.name);  // batch
  setAge(data.age);    // batch -> 1 seul rendu
}

Cas d'usage : Comportement par défaut de toute racine créée avec createRoot : aucune intervention du développeur, un seul rendu par lot de setState.

Anti-pattern : Utiliser flushSync pour forcer un rendu synchrone sans besoin réel (mesure DOM impérative), ce qui annule le gain du batching.
#core#performance#react19

Nouvelle API de react-dom dans React 19.3 (9 septembre 2026). Appelée via use(browser(reason)), elle fait suspendre le composant pendant le rendu serveur (le fallback Suspense part dans le HTML) et ne fait rien dans le navigateur. C'est la réponse officielle au problème des composants dépendant de window ou localStorage en SSR, sans hack typeof window ni useEffect de montage.

Un panneau « ouverture à l'arrivée du personnel » : le serveur laisse la vitrine vide, le navigateur ouvre la boutique.

'use client';
import { use, useState, Suspense } from 'react';
import { browser } from 'react-dom';

function SavedDraft() {
  use(browser('Le brouillon est dans localStorage.'));
  const [draft, setDraft] = useState(() => localStorage.getItem('draft') ?? '');
  return <textarea value={draft} onChange={e => setDraft(e.target.value)} />;
}

export default () => (
  <Suspense fallback={<p>Chargement du brouillon…</p>}>
    <SavedDraft />
  </Suspense>
);

Cas d'usage : Widgets fuseau horaire, thème stocké côté client, éditeurs riches non SSR-compatibles.

Anti-pattern : Appeler browser() sans use() ou sans frontière Suspense au-dessus : le rendu serveur échoue.
#ssr#react19#hooks

Bundle Analysis

React 🟡 Mid

Analyse visuelle du contenu du bundle JavaScript pour identifier les dépendances lourdes et les opportunités de code splitting.

Comme une radio de valise a l'aéroport : tu vois exactement ce qui prend le plus de place dans ton bagage.

// Avec @next/bundle-analyzer
// next.config.js
const withAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});
module.exports = withAnalyzer(nextConfig);
// ANALYZE=true npm run build

Cas d'usage : Diagnostiquer pourquoi le bundle est trop gros et prioriser les optimisations de taille.

#performance#devtools

cache() et cacheSignal

React 🔴 Senior

cache(fn) mémoïse une fonction pour la durée d'une requête serveur, uniquement dans les Server Components : deux composants qui appellent la même fonction avec les mêmes arguments partagent le résultat (dédoublonnage). Le cache est vidé à chaque requête. cacheSignal(), ajouté dans React 19.2 (1er octobre 2025), fournit un AbortSignal qui se déclenche quand la durée de vie du cache se termine, pour annuler les travaux inutiles.

Une commande groupée au restaurant : plusieurs convives demandent le même plat, la cuisine ne le prépare qu'une fois pour ce service.

// lib/data.js — définir UNE fois dans un module partagé
import { cache, cacheSignal } from 'react';

export const getUser = cache(async (id) => {
  const res = await fetch(`/api/users/${id}`, { signal: cacheSignal() });
  return res.json();
});

// Deux Server Components, un seul appel réseau par requête
async function Header({ id }) { const u = await getUser(id); /* ... */ }
async function Sidebar({ id }) { const u = await getUser(id); /* ... */ }

Cas d'usage : Éviter les requêtes BDD dupliquées quand plusieurs Server Components d'une même page ont besoin de la même donnée.

Anti-pattern : Appeler cache(fn) dans le composant (nouveau cache à chaque rendu) ou l'utiliser dans un Client Component (non supporté) ; ne pas confondre avec le 'use cache' persistant de Next.js.
#rsc#react19#performance

Fonctions de next/cache qui pilotent une portée 'use cache' (Next.js 16, 21 octobre 2025). cacheLife(profil) fixe stale (client), revalidate (régénération en arrière-plan) et expire ; profils intégrés seconds, minutes, hours, days, weeks, max, default (5 min / 15 min / jamais). cacheTag(tag) étiquette l'entrée. Côté mutation : revalidateTag(tag, profil) pour du stale-while-revalidate, updateTag(tag) dans une Server Action pour relire immédiatement ses écritures, refresh() pour rafraîchir les données non cachées.

Les dates sur un emballage : « à consommer de préférence » (revalidate) et « à consommer jusqu'au » (expire), plus une étiquette (tag) pour retirer un lot du rayon.

import { cacheLife, cacheTag, updateTag } from 'next/cache';

export async function getPost(slug: string) {
  'use cache';
  cacheLife('days');           // stale 5 min, revalidate 1 j, expire 1 sem
  cacheTag(`post-${slug}`);
  return db.posts.find(slug);
}

'use server';
export async function savePost(slug: string, data: Post) {
  await db.posts.update(slug, data);
  updateTag(`post-${slug}`);   // l'auteur relit sa modif immédiatement
  // revalidateTag('posts', 'max') => SWR ; refresh() => données non cachées
}

Cas d'usage : Blog ou catalogue : longue durée de vie + invalidation à la sauvegarde ; flux qui bouge dans la journée : profil hours ou minutes.

Anti-pattern : Omettre cacheLife (profil default implicite, comportement dépendant des caches imbriqués) ou imbriquer un cache court dans un cache sans durée explicite : erreur au pré-rendu.
#nextjs#caching

Code Splitting

React 🟡 Mid? entretien

Technique qui découpe le bundle JS en morceaux charges a la demande via React.lazy et import() dynamique, réduisant le temps de chargement initial.

Comme un buffet a volonté : tu ne prends que ce que tu vas manger maintenant, pas tout le menu d'un coup.

const AdminPanel = React.lazy(() => import('./AdminPanel'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      {isAdmin && <AdminPanel />}
    </Suspense>
  );
}

Cas d'usage : Pages lourdes (admin, dashboard), composants conditionels, routes non critiques.

Anti-pattern : Splitter des composants minuscules, augmentant le nombre de requêtes HTTP sans gain significatif.
#performance#interview

Component Lifecycle

React 🟢 Junior? entretien

Les différentes phases d'un composant : montage, mise a jour et démontage. En hooks, useEffect gère ces phases.

Comme la vie d'un employé : embauche (mount), promotions (update), départ (unmount).

useEffect(() => {
  console.log('Monte ou mis a jour');
  return () => console.log('Demonte / cleanup');
}, [dep]); // se relance si dep change

Cas d'usage : Gérer les effets de bord comme les abonnements, les appels API ou les timers.

Anti-pattern : Oublier la fonction de cleanup, causant des fuites mémoire avec des abonnements non annules.
#core#interview#hooks

Compound Components

React 🟡 Mid? entretien

Pattern ou un composant parent partage son état implicitement avec ses enfants via Context, offrant une API déclarative et flexible.

Comme un menu déroulant HTML <select>/<option> : les options savent qu'elles font partie du select sans configuration explicite.

// Utilisation declarative
<Tabs>
  <Tabs.List>
    <Tabs.Tab>Profil</Tabs.Tab>
    <Tabs.Tab>Settings</Tabs.Tab>
  </Tabs.List>
  <Tabs.Panels>
    <Tabs.Panel>Contenu profil</Tabs.Panel>
    <Tabs.Panel>Contenu settings</Tabs.Panel>
  </Tabs.Panels>
</Tabs>

Cas d'usage : Composants UI complexes (tabs, accordéons, menus) nécessitant une API flexible et composable.

Anti-pattern : Utiliser ce pattern pour des composants simples ou une prop suffirait.
#patterns#interview#design

Concurrent Rendering

React 🔴 Senior? entretien

Mode de rendu ou React peut interrompre, reprendre et abandonner des rendus en cours pour prioriser les interactions utilisateur urgentes.

Comme un serveur de restaurant qui interrompt la préparation d'une commande pour répondre a un client qui attend a la caisse.

// Active automatiquement avec createRoot
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);

Cas d'usage : Garder l'interface réactivé pendant des rendus lourds (longues listes, calculs complexes).

Anti-pattern : Utiliser ReactDOM.render() legacy qui ne bénéficie pas du rendu concurrent.
#core#interview#performance

Container/Presentational

React 🟢 Junior

Pattern qui sépare les composants en deux types : Container (logique, data fetching) et Presentational (UI pure, reçoit des props). Moins pertinent avec les hooks.

Comme un réalisateur (container) et un acteur (presentational) : l'un dirige, l'autre joue le role.

// Presentational : UI pure
function UserCard({ name, avatar }) {
  return <div><img src={avatar} /><p>{name}</p></div>;
}
// Container : logique
function UserCardContainer({ userId }) {
  const { data } = useQuery({ queryKey: ['user', userId], queryFn: fetchUser });
  return <UserCard name={data?.name} avatar={data?.avatar} />;
}

Cas d'usage : Rendre les composants UI réutilisables et testables en les séparant de la logique.

#patterns#interview

Context API

React 🟡 Mid? entretien

Partage de données sans prop drilling via createContext et un fournisseur. Depuis React 19 (5 décembre 2024), on rend directement <MonContext value={...}> au lieu de <MonContext.Provider>, et use(MonContext) peut remplacer useContext. Attention : chaque changement de valeur re-rend tous les consommateurs.

Comme un haut-parleur dans un magasin : tout le monde entend l'annonce, même ceux qui ne sont pas concernes.

const AuthCtx = createContext(null);

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const value = useMemo(() => ({ user, setUser }), [user]);
  // React 19 : le Context est lui-même le fournisseur
  return <AuthCtx value={value}>{children}</AuthCtx>;
}
function Avatar() {
  const { user } = use(AuthCtx); // ou useContext(AuthCtx)
  return <img src={user?.avatar} alt="" />;
}

Cas d'usage : Données peu changeantes (theme, auth, locale) partagées a travers l'arbre.

Anti-pattern : Stocker du state fréquemment mis a jour dans Context sans le découper, causant des re-rendus en cascade.
#state#interview#core#react19

Controlled vs Uncontrolled

React 🟢 Junior

Controlled : la valeur est gérée par React (via state + onChange). Uncontrolled : la valeur est gérée par le DOM (via ref et defaultValue).

Controlled = GPS qui te guide a chaque virage. Uncontrolled = carte papier que tu consultes quand tu veux.

// Controlled
const [val, setVal] = useState('');
<input value={val} onChange={e => setVal(e.target.value)} />

// Uncontrolled
const ref = useRef();
<input defaultValue="" ref={ref} />
// ref.current.value pour lire

Cas d'usage : Controlled pour validation en temps réel, uncontrolled pour formulaires simples ou performance critique.

Anti-pattern : Passer de controlled a uncontrolled (ou inversement) pendant la vie du composant.
#forms#interview#fundamentals

createAsyncThunk

React 🟡 Mid

Fonction RTK qui génère automatiquement un thunk avec les actions pending/fulfilled/rejected pour les opérations asynchrones.

Comme un livreur avec suivi : tu sais quand il part (pending), arrive (fulfilled) ou échoue (rejected).

const fetchUsers = createAsyncThunk(
  'users/fetch',
  async (_, { rejectWithValue }) => {
    try { return (await api.getUsers()).data; }
    catch (e) { return rejectWithValue(e.message); }
  }
);

Cas d'usage : Appels API avec gestion automatique des états de chargement et d'erreur dans Redux.

Anti-pattern : Ignorer le rejectWithValue et laisser les erreurs non gérées dans les extra reducers.
#state#async

createSelector (RTK)

React 🟡 Mid

Fonction de Reselect (intégrée a RTK) qui crée des sélecteurs memoises. Le résultat n'est recalcule que si les inputs changent, évitant des calculs redondants.

Comme un formulaire pre-rempli : tant que les données source ne changent pas, le calcul dérive est réutilisé.

const selectUsers = (state) => state.users.list;
const selectActiveUsers = createSelector(
  [selectUsers],
  (users) => users.filter(u => u.active)
);
// Ne refiltre que si state.users.list change

Cas d'usage : Dérivation de données coûteuses depuis le store Redux (filtrage, tri, aggregation).

Anti-pattern : Créer un nouveau sélecteur a chaque rendu en l'instanciant dans le composant.
#state#performance

CSR (Client-Side Rendering)

React 🟢 Junior

Le serveur envoie un HTML vide avec un bundle JS. Le navigateur exécuté React pour générer le DOM. Mauvais pour le SEO et le premier affichage.

Comme recevoir des meubles en kit : le client (navigateur) doit tout assembler lui-même avant de pouvoir s'asseoir.

// index.html minimal
// <div id="root"></div>
// <script src="bundle.js"></script>

import { createRoot } from 'react-dom/client';
createRoot(document.getElementById('root')).render(<App />);

Cas d'usage : Dashboards internes, apps behind auth ou le SEO n'est pas critique.

Anti-pattern : Utiliser CSR pour un site public qui a besoin de SEO (blog, e-commerce).
#rendering#interview#architecture

Custom Hook

React 🟡 Mid? entretien

Fonction JavaScript prefixee par 'use' qui encapsulé de la logique réutilisable composée d'autres hooks. Chaque appel créé sa propre instance de state.

Comme une recette de cuisine : tu la réutilisés dans différents repas, mais chaque plat a ses propres ingrédients (state).

function useDebounce(value, delay = 300) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

Cas d'usage : Extraire et réutiliser de la logique stateful entre composants (fetch, debounce, form, auth).

Anti-pattern : Créer un custom hook pour encapsuler du code qui ne contient aucun hook : une simple fonction suffit.
#hooks#interview#patterns

Vulnérabilité critique (CVSS 10.0) divulguée le 3 décembre 2025 : une requête HTTP forgée vers un endpoint de Server Function pouvait, lors de la désérialisation par react-server-dom-webpack, -parcel ou -turbopack (versions 19.0, 19.1.0, 19.1.1, 19.2.0), exécuter du code sur le serveur sans authentification. Toute application RSC était exposée, même sans Server Function explicite. Correctifs : 19.0.1, 19.1.2, 19.2.1 ; Next.js 14.2.35, 15.0.8+, 16.0.11+. Un second avis (DoS et exposition de code source) a suivi le 11 décembre 2025.

Une boîte aux lettres qui exécutait ce qu'on y glissait : il fallait la remplacer, pas juste changer la serrure.

# Vérifier les paquets vulnérables installés
npm ls react-server-dom-webpack react-server-dom-turbopack react-server-dom-parcel

# Corriger : React >= 19.0.1 / 19.1.2 / 19.2.1 et le framework à jour
npm i react@latest react-dom@latest next@latest

# Bonnes pratiques durables
# - autorisation DANS chaque Server Function (pas seulement dans proxy.ts)
# - validation des arguments (zod) : tout ce qui arrive du client est hostile
# - alertes Dependabot/Renovate sur react-server-dom-*

Cas d'usage : Réponse d'entretien sur la surface d'attaque des RSC ; check-list de mise à jour d'urgence dans une équipe qui exploite Next.js ou React Router.

Anti-pattern : Considérer que « pas de 'use server' dans mon code » protège : le point d'entrée existe dès que le framework supporte les RSC.
#security#rsc#react19#interview

Cypress Component Testing

React 🟡 Mid

Mode de test Cypress qui monte des composants React dans un vrai navigateur, permettant des tests visuels et d'interaction sans lancer toute l'application.

Comme tester une pièce de voiture sur un banc d'essai réel au lieu d'une simulation informatique.

import { mount } from 'cypress/react';

it('affiche le compteur', () => {
  mount(<Counter initial={5} />);
  cy.get('[data-testid="count"]').should('have.text', '5');
  cy.get('button').click();
  cy.get('[data-testid="count"]').should('have.text', '6');
});

Cas d'usage : Tests d'interaction complexes (drag & drop, animations) nécessitant un vrai navigateur.

#testing#e2e

Depuis React 19 (5 décembre 2024), <title>, <meta> et <link> rendus n'importe où dans l'arbre sont hissés automatiquement dans le <head>. Les <link rel="stylesheet" precedence="..."> sont dédoublonnées, ordonnées par précédence et attendues avant l'affichage du composant qui en dépend ; les <script async> sont dédoublonnés. Cela réduit le besoin de react-helmet ou d'un système de styles maison en SSR/RSC.

Un courrier interne : vous déposez la note dans n'importe quel service, elle finit épinglée sur le tableau d'affichage de l'entrée.

function BlogPost({ post }) {
  return (
    <article>
      <title>{post.title}</title>                 {/* hissé dans <head> */}
      <meta name="author" content={post.author} />
      <link rel="stylesheet" href="/article.css" precedence="default" />
      <script async src="/embed.js" />          {/* dédoublonné */}
      <h1>{post.title}</h1>
    </article>
  );
}

Cas d'usage : SEO par page dans une app SSR/RSC, chargement de CSS par composant sans FOUC, scripts tiers embarqués une seule fois.

Anti-pattern : Rendre <link rel="stylesheet"> sans attribut precedence : React ne gère alors ni l'ordre ni la déduplication.
#core#react19#seo

EntityAdapter (RTK)

React 🟡 Mid

Utilitaire RTK qui génère un ensemble de reducers et sélecteurs pour gérer des collections normalisées (ids + entities) avec opérations CRUD pre-construites.

Comme un classeur a fiches avec index : ajout, suppression et recherche sont automatises et optimises.

const usersAdapter = createEntityAdapter();
const usersSlice = createSlice({
  name: 'users',
  initialState: usersAdapter.getInitialState(),
  reducers: {
    addUser: usersAdapter.addOne,
    removeUser: usersAdapter.removeOne,
  }
});

Cas d'usage : Collections d'entités (utilisateurs, produits, commentaires) nécessitant des opérations CRUD normalisées.

#state#patterns

Error Boundary

React 🟡 Mid? entretien

Composant classe qui intercepte les erreurs de rendu de son sous-arbre et affiche une UI de secours ; il n'existe toujours pas d'équivalent hook. React 19 (5 décembre 2024) ajoute aux racines les options onCaughtError et onUncaughtError et ne relance plus les erreurs de rendu (elles vont à window.reportError ou console.error). Next.js 16.3 (3 août 2026) fournit catchError (next/error) : une frontière qui n'interfère pas avec notFound/redirect et expose retry() pour re-rendre des Server Components en échec.

Comme un fusible électrique : il saute pour protéger le reste du circuit.

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  componentDidCatch(error, info) { logToService(error, info.componentStack); }
  render() {
    return this.state.hasError ? <p>Oups !</p> : this.props.children;
  }
}
// React 19 : journalisation globale à la racine
createRoot(el, {
  onCaughtError: (err, info) => logToService(err, info),
  onUncaughtError: (err, info) => logToService(err, info),
}).render(<App />);

Cas d'usage : Encapsuler des sections critiques pour que le reste de l'app continue de fonctionner en cas d'erreur.

Anti-pattern : Ne pas logger l'erreur dans componentDidCatch, perdant des informations précieuses en production.
#core#interview#resilience

Depuis React 19.2 (1er octobre 2025), eslint-plugin-react-hooks v6 utilise la flat config par défaut et embarque des règles issues du compilateur : set-state-in-render (boucles de rendu), set-state-in-effect (travail coûteux dans un effet), refs (lecture de ref pendant le rendu), en plus de rules-of-hooks et exhaustive-deps. Ces règles encodent les Rules of React que le compilateur exige.

Le contrôle technique avant la course : la voiture (le compilateur) ne part que si les voyants sont éteints.

// eslint.config.js (flat config)
import reactHooks from 'eslint-plugin-react-hooks';
export default [
  reactHooks.configs.flat.recommended,
];

// Exemple d'erreur détectée : set-state-in-render
function Counter({ start }) {
  const [n, setN] = useState(0);
  if (n < start) setN(start); // ❌ setState pendant le rendu => boucle
  return <p>{n}</p>;
}

Cas d'usage : Préparer une base de code à React Compiler et attraper les bugs de pureté avant la production.

Anti-pattern : Désactiver exhaustive-deps « pour éviter les re-runs » : cela masque des closures périmées et rend le composant non compilable.
#tooling#quality#react19

Fin de Create React App

React 🟢 Junior

Create React App est officiellement déprécié depuis le 14 février 2025 : plus de mainteneurs actifs, pas de routage, de data fetching ni de code splitting intégrés. L'équipe React recommande un framework (Next.js, React Router en mode framework, Expo) ou, à défaut, un outil de build (Vite, Parcel, Rsbuild). Le paquet reste installable en mode maintenance avec un avertissement.

La vieille voiture de l'auto-école : elle roule encore, mais plus personne ne la répare et on n'apprend plus dessus.

# ❌ Déprécié depuis le 14/02/2025
npx create-react-app mon-app

# ✅ Framework (recommandé par l'équipe React)
npx create-next-app@latest mon-app
npx create-react-router@latest mon-app

# ✅ Outil de build seul (SPA)
npm create vite@latest mon-app -- --template react-ts

Cas d'usage : Question d'entretien : « comment démarres-tu un projet React aujourd'hui ? » ; migration d'un vieux projet CRA vers Vite ou un framework.

Anti-pattern : Proposer CRA dans un kata ou un test technique : cela signale une veille technologique arrêtée.
#tooling#ecosystem#interview

Fragment

React 🟢 Junior

Conteneur invisible qui permet de retourner plusieurs éléments sans ajouter de noeud DOM supplémentaire.

Comme un trombone pour regrouper des feuilles : il les tient ensemble sans ajouter de page vide.

function Info() {
  return (
    <>
      <h1>Titre</h1>
      <p>Description</p>
    </>
  );
}

Cas d'usage : Éviter les divs inutiles qui cassent le layout CSS ou la sémantique HTML.

#core#syntax

Fragment Refs

React 🔴 Senior

Stable depuis React 19.3 (9 septembre 2026) : <Fragment ref={...}> (syntaxe explicite, pas <>) reçoit une FragmentInstance qui agit sur les nœuds DOM enfants de premier niveau sans élément englobant : addEventListener, focus/focusLast/blur, observeUsing (IntersectionObserver, ResizeObserver), getClientRects, scrollIntoView. Utile pour les composants qui ne veulent pas imposer de <div> wrapper.

Une télécommande universelle qui pilote plusieurs appareils posés côte à côte, sans avoir à les mettre dans la même armoire.

import { Fragment, useRef, useEffect } from 'react';
function Headings({ posts }) {
  const ref = useRef(null);
  useEffect(() => {
    ref.current.focus();                 // premier focusable
    const obs = new IntersectionObserver(onSeen);
    ref.current.observeUsing(obs);       // enfants DOM de 1er niveau
    return () => ref.current?.unobserveUsing(obs);
  }, []);
  return (
    <Fragment ref={ref}>   {/* syntaxe explicite, pas <> */}
      {posts.map(p => <h2 key={p.id} tabIndex={0}>{p.title}</h2>)}
    </Fragment>
  );
}

Cas d'usage : Gestion du focus et observation de visibilité dans des listes ou des composants headless sans wrapper qui casse la grille CSS.

Anti-pattern : Attendre que les méthodes atteignent les descendants profonds : hors focus(), elles ne ciblent que les enfants DOM de premier niveau.
#core#react19#dom

Headless Components

React 🔴 Senior

Composants qui fournissent la logique et l'accessibilite sans imposer de style ni de markup. L'utilisateur contrôle entièrement le rendu.

Comme un moteur de voiture vendu sans carrosserie : tu choisis la forme, il fournit la puissance.

// Headless UI Combobox
<Combobox value={selected} onChange={setSelected}>
  <Combobox.Input onChange={e => setQuery(e.target.value)} />
  <Combobox.Options>
    {filtered.map(item => (
      <Combobox.Option key={item.id} value={item}>{item.name}</Combobox.Option>
    ))}
  </Combobox.Options>
</Combobox>

Cas d'usage : Design systems, librairies UI (Headless UI, Radix, Downshift) qui doivent être stylisables sans contrainte.

#patterns#design-system#a11y

HOC (Higher-Order Component)

React 🟡 Mid? entretien

Fonction qui prend un composant et retourne un nouveau composant enrichi. Pattern classique de réutilisation de logique, largement remplace par les hooks.

Comme un cadre photo qui embellit toute photo qu'on y met : le HOC ajoute des fonctionnalités a tout composant.

function withAuth(Component) {
  return function AuthWrapper(props) {
    const { user } = useAuth();
    if (!user) return <Redirect to="/login" />;
    return <Component {...props} user={user} />;
  };
}
const ProtectedPage = withAuth(Dashboard);

Cas d'usage : Ajouter de la logique transverse (auth, logging, theming) a plusieurs composants.

Anti-pattern : Empiler trop de HOCs, créant un 'wrapper hell' et rendant le debugging difficile.
#patterns#interview

Infinite Query

React 🟡 Mid

Pattern TanStack Query pour le scroll infini ou le 'charger plus'. useInfiniteQuery gère automatiquement les pages, le curseur et la concatenation des résultats.

Comme un rouleau de papier toilette : tu en deroules plus quand tu en as besoin, page après page.

const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: ['posts'],
  queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
  getNextPageParam: (lastPage) => lastPage.nextCursor,
});
// data.pages contient toutes les pages chargees

Cas d'usage : Feeds sociaux, listes de produits, tout contenu pagine avec scroll infini ou bouton 'charger plus'.

#data-fetching#ux

Ensemble opt-in de Next.js 16.3 (3 août 2026) qui redonne aux apps Server Components la réactivité d'une SPA : avec cacheComponents et partialPrefetching activés, Next.js extrait une coquille de chargement de n'importe quelle route (Suspense ou 'use cache') et la précharge côté client. Les DevTools ajoutent Instant Insights (navigations lentes) et un Navigation Inspector ; @next/playwright fournit instant() pour tester ce qui doit s'afficher sans réseau. Ces comportements deviendront le défaut d'une future version majeure.

Le décor du prochain acte déjà monté en coulisses : au changement de scène, le rideau s'ouvre sans temps mort.

// next.config.ts
export default { cacheComponents: true, partialPrefetching: true };

// Test Playwright : ce qui doit être visible instantanément
import { test, expect } from '@playwright/test';
import { instant } from '@next/playwright';

test('titre produit immédiat', async ({ page }) => {
  await page.goto('/products/shoes');
  await instant(page, async () => {
    await page.click('a[href="/products/hats"]');
    await expect(page.locator('h1')).toContainText('Casquette');
  });
});

Cas d'usage : E-commerce ou dashboard où les clics semblaient lents malgré les Server Components ; garantir par test qu'un refactor ne rend pas une navigation bloquante.

Anti-pattern : Compter sur un loading.tsx par route et en oublier un : la navigation redevient bloquante sans alerte (d'où Instant Insights).
#nextjs#performance#ux

Intercepting Routes

React 🔴 Senior

Permet d'intercepter une route pour afficher son contenu dans le layout actuel (ex: modale) au lieu de naviguer vers la page complète.

Comme un aperçu rapide au survol : tu vois le contenu sans quitter ta page, mais l'URL directe montre la version complète.

// app/@modal/(.)photo/[id]/page.tsx
// Le (.) intercepte /photo/[id]
export default function PhotoModal({ params }) {
  return (
    <Modal>
      <Photo id={params.id} />
    </Modal>
  );
}
// URL directe /photo/123 -> page complete

Cas d'usage : Photos en modale (Instagram-like), aperçu de produit, détail de notification sans quitter le feed.

#nextjs#routing#advanced

invalidateQueries

React 🟡 Mid

Méthode TanStack Query qui marque des requêtes en cache comme périmées, déclenchant un refetch automatique si elles sont actuellement utilisées.

Comme tirer la chasse d'eau du cache : les données périmées sont évacuées et de nouvelles arrivent.

const queryClient = useQueryClient();

// Invalider toutes les requetes 'users'
queryClient.invalidateQueries({ queryKey: ['users'] });

// Invalider un user specifique
queryClient.invalidateQueries({ queryKey: ['users', userId] });

Cas d'usage : Après une mutation réussie, forcer le rechargement des données affectées dans le cache.

Anti-pattern : Invalider trop large (toutes les queries) au lieu de cibler les queryKeys spécifiques.
#data-fetching

Combinaison SSG + SSR : la page est générée statiquement puis régénérée en arrière-plan après un délai (revalidate). Introduit par Next.js. Depuis Next.js 16 (21 octobre 2025), revalidateTag exige un profil cacheLife en second argument ('max', 'hours'…) pour un comportement stale-while-revalidate, et Next.js 16.3 (3 août 2026) permet de servir une coquille de chargement instantanée au premier visiteur d'une URL non pré-rendue, puis de la promouvoir en page pré-rendue.

Comme un menu du jour affiche a l'entrée : il est pre-imprime mais le serveur le remplace discrètement chaque jour.

// Next.js App Router : régénération toutes les heures
export const revalidate = 3600;

export default async function Products() {
  const products = await getProducts();
  return <ProductGrid products={products} />;
}

// Invalidation à la demande (Next.js 16 : profil obligatoire)
import { revalidateTag } from 'next/cache';
revalidateTag('products', 'max');

Cas d'usage : Pages e-commerce, catalogues : contenu relativement stable mais qui doit se mettre a jour sans rebuild complet.

#rendering#performance#nextjs

Jotai

React 🟡 Mid

Gestion d'état atomique pour React. Chaque atome est une unité de state indépendante, composable et réactive, sans Provider obligatoire.

Comme des briques LEGO : chaque brique (atome) est indépendante et tu les assembles pour construire ton state.

const countAtom = atom(0);
const doubleAtom = atom((get) => get(countAtom) * 2);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

Cas d'usage : State atomique granulaire quand chaque composant n'a besoin que d'une fraction de l'état global.

#state#ecosystem

JSX

React 🟢 Junior? entretien

Syntaxe qui permet d'écrire du HTML dans du JavaScript. Le compilateur le transforme en appels React.createElement().

C'est comme un moule a gâteau : tu dessines la forme en HTML, et React coule le JavaScript dedans.

// JSX compile en createElement
const el = <h1 className="title">Bonjour</h1>;
// equivalent a :
const el2 = React.createElement('h1', { className: 'title' }, 'Bonjour');

Cas d'usage : Utilise dans chaque composant React pour décrire l'interface de manière déclarative.

Anti-pattern : Oublier que class s'écrit className et for s'écrit htmlFor en JSX.
#core#interview#syntax

Key Prop

React 🟢 Junior

Attribut spécial qui aide React a identifier chaque élément dans une liste. Permet une réconciliation efficace sans reconstruire tout le DOM.

Comme les numéros de dossards dans une course : React sait qui est qui même si l'ordre change.

// Correct: identifiant unique
items.map(item => <li key={item.id}>{item.name}</li>)

// Anti-pattern: index comme key
items.map((item, i) => <li key={i}>{item.name}</li>)

Cas d'usage : Obligatoire dans toute liste dynamique pour éviter des bugs de rendu et améliorer les performances.

Anti-pattern : Utiliser l'index comme key dans une liste qui peut être triée, filtrée ou modifiée, causant des bugs d'état.
#core#interview#performance

Loader (React Router)

React 🟡 Mid

Fonction associée à une route qui charge les données avant le rendu du composant (modes data et framework de React Router). En mode framework (depuis la v7, 22 novembre 2024), le composant reçoit loaderData typé en prop via les types générés Route.LoaderArgs et Route.ComponentProps.

Comme un serveur qui prépare ta table avant que tu arrives : tout est prêt quand tu t'assois.

// app/routes/user.tsx (framework mode, v7+)
import type { Route } from './+types/user';

export async function loader({ params }: Route.LoaderArgs) {
  const user = await getUser(params.id);
  return { user };            // plus besoin de json()
}

export default function User({ loaderData }: Route.ComponentProps) {
  return <p>{loaderData.user.name}</p>;   // typé automatiquement
}
// Mode data : useLoaderData() reste disponible

Cas d'usage : Pre-charger les données d'une page avant le rendu pour éviter les waterfalls de fetching.

#routing#data-fetching

Middleware (React Router v8)

React 🔴 Senior

Fonctions exécutées autour des loaders et actions d'une route, activées par défaut depuis React Router v8 (17 juin 2026, plus de flag future.v8_middleware) en modes data et framework. Le middleware serveur reçoit { request, context } et next(), s'enchaîne parent → enfant puis remonte, et doit retourner une Response. Le contexte est typé via createContext / context.set / context.get ; un clientMiddleware existe pour le navigateur.

Les sas successifs d'un bâtiment sécurisé : chaque porte vérifie quelque chose avant de laisser passer vers la salle, et on ressort par les mêmes portes.

import { createContext, redirect } from 'react-router';
import type { Route } from './+types/dashboard';

export const userContext = createContext<User | null>(null);

async function auth({ request, context }: Route.MiddlewareArgs, next) {
  const user = await getUserFromSession(request);
  if (!user) throw redirect('/login');
  context.set(userContext, user);
  return next();                 // Response obligatoire côté serveur
}
export const middleware: Route.MiddlewareFunction[] = [auth];
export async function loader({ context }: Route.LoaderArgs) {
  return { user: context.get(userContext) }; // garanti non nul
}

Cas d'usage : Authentification et autorisation par segment de route, journalisation, injection de dépendances (client BDD) dans les loaders.

Anti-pattern : Refaire la vérification de session dans chaque loader au lieu d'un middleware parent ; oublier de retourner la Response côté serveur.
#routing#auth#architecture

MSW (Mock Service Worker)

React 🟡 Mid

Outil qui intercepte les requêtes réseau au niveau du Service Worker pour les mocker, sans modifier le code applicatif. Fonctionne en test et en dev.

Comme un doubleur de voix au cinéma : le vrai acteur (API) est absent mais la scène (test) se joue quand même.

import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  http.get('/api/users', () => HttpResponse.json([
    { id: 1, name: 'Alice' }
  ]))
);
beforeAll(() => server.listen());

Cas d'usage : Mocker les API dans les tests et pendant le développement frontend sans backend disponible.

#testing#mocking

Mutation (TanStack Query)

React 🟡 Mid

Opération d'écriture (POST, PUT, DELETE) gérée par useMutation avec callbacks onSuccess/onError et possibilité de mises a jour optimistes.

Comme un formulaire de La Poste : tu le remplis (mutate), tu sais si c'est envoyé (onSuccess) ou perdu (onError).

const mutation = useMutation({
  mutationFn: (newUser) => api.post('/users', newUser),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['users'] });
  },
});
mutation.mutate({ name: 'Alice' });

Cas d'usage : Toute opération d'écriture vers le serveur avec invalidation automatique du cache après succès.

#data-fetching

Nested Routes

React 🟡 Mid

Routes imbriquées ou les routes enfants s'affichent a l'intérieur du layout de leur route parent via Outlet, permettant des layouts partages et une navigation hiérarchique.

Comme des poupées russes : chaque layout contient le suivant, et seul le contenu le plus interne change.

// Mode data (v7/v8) : `Component` remplace `element`
// /dashboard -> DashLayout + DashHome
// /dashboard/settings -> DashLayout + Settings
const router = createBrowserRouter([
  {
    path: 'dashboard',
    Component: DashLayout,
    children: [
      { index: true, Component: DashHome },
      { path: 'settings', Component: Settings },
    ],
  },
]);

Cas d'usage : Applications avec des layouts hiérarchiques (sidebar fixe, breadcrumbs dynamiques, multi-step wizards).

#routing

Next.js App Router

React 🟡 Mid? entretien

Système de routage Next.js fondé sur le système de fichiers, avec Server Components par défaut, layouts imbriqués et conventions (page, layout, loading, error). Version stable actuelle : Next.js 16.3 (3 août 2026) ; Next.js 16 (21 octobre 2025) a rendu Turbopack bundler par défaut, exige Node.js 20.9+, impose l'accès asynchrone à params, searchParams, cookies() et headers(), et exige un fichier default.js pour chaque slot de route parallèle.

Comme un immeuble ou chaque dossier est un étage et chaque fichier est une pièce avec un role précis.

// app/users/[id]/page.tsx
export default async function UserPage({ params }) {
  const { id } = await params;
  const user = await getUser(id);
  return <h1>{user.name}</h1>;
}
// app/users/[id]/loading.tsx -> Suspense automatique
// app/users/[id]/error.tsx -> Error Boundary auto

Cas d'usage : Applications React full-stack avec SSR, RSC, routing et API integres.

Anti-pattern : Lire params ou searchParams de façon synchrone (supprimé en Next.js 16) ; laisser un middleware.ts déprécié au lieu de proxy.ts.
#nextjs#interview#framework

Next.js Middleware

React 🟡 Mid

Code exécuté avant le rendu d'une route pour rediriger, réécrire l'URL, poser des en-têtes ou des cookies. Depuis Next.js 16 (21 octobre 2025), le fichier middleware.ts est déprécié et renommé proxy.ts ; il s'exécute par défaut sur le runtime Node.js et non plus Edge.

Comme un vigile a l'entrée d'un bâtiment : il vérifie ton badge avant de te laisser entrer dans n'importe quelle pièce.

// proxy.ts (racine du projet) — Next.js 16+
import { NextResponse, type NextRequest } from 'next/server';

export function proxy(request: NextRequest) {
  const token = request.cookies.get('token');
  if (!token) return NextResponse.redirect(new URL('/login', request.url));
  return NextResponse.next();
}
export const config = { matcher: ['/dashboard/:path*'] };
// Migration : npx @next/codemod@canary middleware-to-proxy .

Cas d'usage : Redirections, réécritures, en-têtes CORS, vérification légère de session sur un groupe de routes via matcher.

Anti-pattern : Faire reposer l'authentification uniquement sur le proxy : les Server Functions sont des POST sur la route qui les utilise, et un matcher qui exclut un chemin les laisse passer. Vérifier l'autorisation dans chaque Server Function.
#nextjs#auth#routing

Optimistic UI Forms

React 🟡 Mid

Pattern combinant useOptimistic et useFormStatus pour afficher immédiatement le résultat d'une soumission de formulaire avant la confirmation serveur.

Comme un tchat ou ton message s'affiche instantanément avec une horloge, puis la coche de confirmation arrive du serveur.

function TodoForm({ addTodo }) {
  const [optimistic, setOptimistic] = useOptimistic(todos);
  async function action(formData) {
    setOptimistic(prev => [...prev, { text: formData.get('text'), pending: true }]);
    await addTodo(formData);
  }
  return <form action={action}><input name="text" /><SubmitBtn /></form>;
}

Cas d'usage : Formulaires ou la latence perçue nuit a l'UX : chat, commentaires, listes de taches.

#forms#ux#react19

Optimistic Update

React 🟡 Mid

Technique qui met a jour l'UI immédiatement avant la confirmation serveur, puis annule en cas d'échec. Amélioré la perception de vitesse.

Comme célébrer un but avant la validation VAR : si le but est annule, on revient a l'état précédent.

useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    const prev = queryClient.getQueryData(['todos']);
    queryClient.setQueryData(['todos'], old => [...old, newTodo]);
    return { prev };
  },
  onError: (_, __, ctx) => queryClient.setQueryData(['todos'], ctx.prev),
});

Cas d'usage : Actions utilisateur fréquentes (like, toggle, reorder) ou la latence réseau dégrade l'UX.

#data-fetching#ux#patterns

Outlet

React 🟡 Mid

Composant React Router qui rend la route enfant correspondante dans un layout parent, permettant des layouts imbriques partageant du UI commun.

Comme un cadre photo dans un salon : le salon (layout) reste, mais la photo (enfant) change selon la page.

import { Outlet } from 'react-router'; // v7+/v8 : plus de react-router-dom

function Layout() {
  return (
    <div>
      <Navbar />
      <main><Outlet /></main> {/* la route enfant se rend ici */}
      <Footer />
    </div>
  );
}

Cas d'usage : Layouts partages (sidebar, header, footer) avec du contenu qui change selon la route active.

#routing

captureOwnerStack() (développement uniquement, retourne null en production) renvoie la pile des composants qui ont créé l'élément courant, et non de ceux qui l'ont simplement rendu ou transmis via children. Plus précise qu'une component stack pour remonter à l'origine d'une erreur ; disponible dans le rendu, les effets, les gestionnaires d'événements React et les callbacks d'erreur de la racine.

La chaîne de responsabilité d'un colis : qui l'a expédié, pas la liste des entrepôts traversés.

import * as React from 'react';

const original = console.error;
console.error = (...args) => {
  original(...args);
  if (process.env.NODE_ENV !== 'production') {
    const owners = React.captureOwnerStack(); // null hors contexte React
    if (owners) sendToOverlay({ message: args[0], owners });
  }
};
// Component Stack : SubComponent > fieldset > Component > App
// Owner Stack     : Component (celui qui a créé <SubComponent/>)

Cas d'usage : Outils de développement, overlays d'erreur de framework, debug d'un composant rendu à des endroits multiples.

Anti-pattern : L'appeler dans un setTimeout ou un écouteur DOM natif : hors contexte React, le résultat est null.
#debugging#react19#tooling

Parallel Routes

React 🔴 Senior

Fonctionnalité Next.js App Router qui permet de rendre plusieurs pages simultanément dans le même layout via des slots nommes (@slot).

Comme un écran divise en deux sur ta TV : tu regardes deux chaînés en même temps dans le même cadre.

// app/layout.tsx
export default function Layout({ children, analytics, team }) {
  return (
    <div>
      {children}
      {analytics} {/* @analytics/page.tsx */}
      {team}       {/* @team/page.tsx */}
    </div>
  );
}

Cas d'usage : Dashboards avec plusieurs panneaux indépendants, modales comme routes, vues conditionnelles.

#nextjs#routing#advanced

Partial Prerendering

React 🔴 Senior

Rendu qui combine une coquille statique servie depuis le CDN et des « trous » dynamiques remplis en streaming via Suspense. Dans Next.js 16 (21 octobre 2025), le flag expérimental.ppr a été supprimé : le PPR est intégré au modèle « Cache Components » (cacheComponents: true, directive 'use cache'). Côté React, les API prerender / résume / resumeAndPrerender de react-dom (React 19.2, 1er octobre 2025) fournissent la brique bas niveau.

Comme un journal avec des encarts pub personnalises : le journal est imprime d'avance, mais les pubs changent par lecteur.

// next.config.ts
export default { cacheComponents: true };

// app/product/[id]/page.tsx
export default async function Page({ params }) {
  const { id } = await params;
  return (
    <>
      <ProductInfo id={id} />          {/* 'use cache' => coquille statique */}
      <Suspense fallback={<CartSkeleton />}>
        <Cart />                        {/* dynamique, streamé */}
      </Suspense>
    </>
  );
}

Cas d'usage : Page e-commerce : en-tête, navigation et fiche produit pré-rendus ; panier et prix personnalisés streamés à la requête.

Anti-pattern : Lire cookies() ou headers() dans une portée 'use cache' : cela échoue avec l'erreur next-request-in-use-cache ; lire ces valeurs à l'extérieur et les passer en argument.
#rendering#nextjs#performance

placeholderData

React 🟡 Mid

Option TanStack Query qui fournit des données temporaires pendant le chargement initial, sans les stocker dans le cache. Évite le flash de loading.

Comme une maquette de vitrine : elle montre la disposition avant que les vrais produits arrivent.

useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
  placeholderData: (previousData) => previousData,
  // ou: placeholderData: { name: 'Chargement...', email: '' }
});

Cas d'usage : Afficher les données précédentés pendant une transition entre pages ou profils utilisateur.

#data-fetching#ux

Polymorphic Components

React 🔴 Senior

Composants qui peuvent changer leur élément HTML racine via une prop 'as' ou 'component', tout en conservant un typage TypeScript correct.

Comme un costume transformable : le même vêtement peut devenir veste, gilet ou manteau selon le bouton presse.

function Box<C extends React.ElementType = 'div'>(
  { as, children, ...props }: { as?: C } & React.ComponentPropsWithoutRef<C>
) {
  const Component = as || 'div';
  return <Component {...props}>{children}</Component>;
}
<Box as="section">...</Box>
<Box as="a" href="/">Lien</Box>

Cas d'usage : Design systems ou un même composant stylise doit s'adapter a différents éléments HTML sémantiques.

#patterns#typescript#design-system

Portal

React 🟡 Mid

Permet de rendre un composant enfant dans un noeud DOM situe en dehors de la hiérarchie du parent. Les evenements remontent toujours dans l'arbre React.

Comme un tunnel secret : le composant vit dans la maison React mais sort par une porte cachée dans un autre endroit du DOM.

import { createPortal } from 'react-dom';

function Modal({ children }) {
  return createPortal(
    <div className="modal">{children}</div>,
    document.getElementById('modal-root')
  );
}

Cas d'usage : Modales, tooltips, menus déroulants qui doivent échapper au overflow:hidden du parent.

Anti-pattern : Utiliser un portal sans gérer le focus trapping pour l'accessibilite.
#core#advanced#a11y

prefetchQuery

React 🟡 Mid

Méthode de TanStack Query pour pre-charger des données en cache avant qu'elles ne soient nécessaires, éliminant le temps de chargement visible.

Comme préchauffer le four avant d'enfourner : quand tu en as besoin, tout est déjà prêt.

// Prefetch au hover d'un lien
<Link
  onMouseEnter={() => {
    queryClient.prefetchQuery({
      queryKey: ['user', userId],
      queryFn: () => fetchUser(userId),
    });
  }}
/>

Cas d'usage : Pre-charger les données de la prochaine page au survol d'un lien ou pendant un formulaire multi-étapes.

#data-fetching#performance

prerender() (React 19, 5 décembre 2024) génère du HTML statique en attendant la résolution de toutes les frontières Suspense, et retourne { prélude, postponed }. Depuis React 19.2 (1er octobre 2025), résume() reprend un pré-rendu interrompu (via AbortSignal) pour terminer le rendu à la requête, et resumeAndPrerender() reprend vers du statique : c'est la brique React du Partial Prerendering. Ces API fonctionnent en Web Streams, y compris sous Node.js depuis 19.2.

Une pâte à pizza précuite : la base est prête à l'avance, on n'ajoute la garniture fraîche qu'à la commande.

import { prerender } from 'react-dom/static';
import { resume } from 'react-dom/server';

// Au build : coquille statique, arrêt après 10 s
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 10_000);
const { prelude, postponed } = await prerender(<App />, {
  signal: ctrl.signal, bootstrapScripts: ['/main.js'],
});
await saveToCDN(prelude, postponed);

// À la requête : on reprend là où le pré-rendu s'est arrêté
const stream = await resume(<App />, postponed);

Cas d'usage : SSG classique (prerender seul) ou PPR maison hors Next.js ; comprendre ce que Next.js Cache Components fait sous le capot.

Anti-pattern : Utiliser renderToString pour du statique : il n'attend pas Suspense et ne produit pas de postponed.
#ssr#react19#rendering

Profiler API

React 🟡 Mid

Composant React et onglet DevTools qui mesurent le coût de rendu d'un sous-arbre : fréquence, durée et raison des re-rendus.

Comme un chronomètre pour chaque coureur dans un relais : tu identifies qui ralentit l'équipe.

<Profiler id="Navigation" onRender={(id, phase, duration) => {
  console.log(`${id} ${phase}: ${duration.toFixed(1)}ms`);
}}>
  <Nav items={items} />
</Profiler>

Cas d'usage : Identifier les composants les plus lents pour cibler les optimisations de performance.

#performance#devtools

Progressive Enhancement

React 🟡 Mid

Principe ou l'application fonctionne sans JavaScript (formulaires HTML natifs, liens) puis s'améliore progressivement avec JS pour une meilleure UX.

Comme un escalier avec un escalator a cote : ca marche a pied (sans JS), mais c'est mieux avec le moteur (JS).

// Le formulaire fonctionne meme sans JS
<form action={serverAction} method="POST">
  <input name="email" required />
  <SubmitButton /> {/* desactive pendant l'envoi si JS actif */}
</form>

Cas d'usage : Applications critiques (e-commerce, admin) qui doivent rester fonctionnelles même si le JS échoué.

#rendering#a11y#bestpractice

Prop Getter Pattern

React 🔴 Senior

Pattern qui fournit des fonctions (getInputProps, getToggleProps) retournant les props nécessaires pour un élément, fusionnant les props utilisateur avec celles du composant.

Comme un styliste qui te donne un ensemble complet : tu peux ajouter tes accessoires, il gère l'harmonie du tout.

function useSelect() {
  const [isOpen, setOpen] = useState(false);
  const getToggleProps = (userProps = {}) => ({
    'aria-expanded': isOpen,
    onClick: () => { userProps.onClick?.(); setOpen(!isOpen); },
    ...userProps,
  });
  return { isOpen, getToggleProps };
}
<button {...getToggleProps({ className: 'btn' })}>Toggle</button>

Cas d'usage : Composants headless et librairies qui doivent gérer l'accessibilite tout en restant flexibles.

#patterns#advanced#a11y

Props

React 🟢 Junior? entretien

Données passées d'un composant parent a un composant enfant, en lecture seule. C'est le mécanisme principal de communication descendante.

Comme les ingrédients donnes a un cuisinier : il les utilise mais ne les modifie pas pour le fournisseur.

function Card({ title, children }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      {children}
    </div>
  );
}
<Card title="Bienvenue">Contenu ici</Card>

Cas d'usage : Rendre un composant réutilisable en parametrant son comportement et son contenu.

Anti-pattern : Passer trop de props (prop drilling) au lieu d'utiliser Context ou la composition.
#core#interview#fundamentals

Provider Pattern

React 🟡 Mid? entretien

Pattern qui utilise Context.Provider pour fournir des données ou des services a un sous-arbre de composants, sans prop drilling.

Comme le Wi-Fi d'un bâtiment : tous les appareils dans la zone y accèdent sans câble direct.

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}
// Usage: <ThemeProvider><App /></ThemeProvider>

Cas d'usage : Fournir des services globaux (theme, i18n, auth, feature flags) a toute l'application.

Anti-pattern : Créer un 'Provider Hell' avec 15 providers imbriques a la racine sans les composer.
#patterns#interview

Depuis Next.js 16 (21 octobre 2025), le fichier middleware.ts est renommé proxy.ts et sa fonction exportée proxy ; il s'exécute par défaut sur Node.js. Il intercepte les requêtes avant le rendu pour rediriger, réécrire, poser des en-têtes ou cookies, filtré par un matcher. Le renommage vise à clarifier son rôle de frontière réseau et à décourager son usage pour de la logique métier.

Le portier d'immeuble : il oriente et vérifie le badge à l'entrée, mais ne fait pas le travail des bureaux.

// proxy.ts
import { NextResponse, type NextRequest } from 'next/server';

export function proxy(request: NextRequest) {
  if (!request.cookies.has('session')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  return NextResponse.next();
}
export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
// Codemod : npx @next/codemod@canary middleware-to-proxy .

Cas d'usage : Redirections de session, en-têtes CORS, A/B testing par cookie, réécritures d'URL legacy.

Anti-pattern : Oublier le matcher (le proxy tourne alors sur chaque asset) ou déléguer toute l'autorisation au proxy alors que les Server Functions doivent se protéger elles-mêmes.
#nextjs#routing#auth

queryKey

React 🟡 Mid

Identifiant unique d'une requête dans TanStack Query, utilise pour le cache, la deduplication et l'invalidation. Généralement un tableau hiérarchique.

Comme l'étiquette d'un tiroir de rangement : elle dit exactement ce qu'il y a dedans et permet de le retrouver.

// Hierarchique pour une invalidation fine
useQuery({ queryKey: ['users'] });
useQuery({ queryKey: ['users', userId] });
useQuery({ queryKey: ['users', userId, 'posts'] });

// Invalider tout ce qui commence par 'users'
queryClient.invalidateQueries({ queryKey: ['users'] });

Cas d'usage : Structurer les clés de façon hiérarchique pour pouvoir invalider a différents niveaux de granularite.

Anti-pattern : Utiliser des strings simples au lieu de tableaux hiérarchiques, rendant l'invalidation partielle impossible.
#data-fetching

React Compiler

React 🔴 Senior? entretien

Compilateur de build (plugin Babel babel-plugin-react-compiler) stable en version 1.0 depuis le 7 octobre 2025. Il analyse le flux de données de chaque composant et hook et insère la mémoïsation automatiquement, rendant useMemo, useCallback et React.memo optionnels. Il cible React 19 mais supporte React 17 et 18 via react-compiler-runtime. Les composants qui violent les Rules of React sont simplement laissés non compilés.

Un correcteur orthographique qui réécrit vos phrases lourdes à la volée, mais laisse tranquilles celles qu'il ne comprend pas.

// npm i -D babel-plugin-react-compiler@latest eslint-plugin-react-hooks@latest
// babel.config.js — doit être le PREMIER plugin
module.exports = { plugins: ['babel-plugin-react-compiler'] };

// next.config.ts (Next.js 16 : option stable)
export default { reactCompiler: true };

// Exclure un composant problématique
function Legacy() {
  'use no memo';
  // ...
}
// Vérification : badge « Memo ✨ » dans React DevTools

Cas d'usage : Supprimer la mémoïsation manuelle d'une base de code et ses bugs de dépendances ; Meta rapporte jusqu'à 12 % de chargement initial en moins et des interactions 2,5× plus rapides.

Anti-pattern : Activer le compilateur sans tests de bout en bout ni respect des Rules of React : le lint eslint-plugin-react-hooks v6 doit être vert d'abord. Épingler la version exacte (--save-exact).
#tooling#performance#react19#interview

React Fiber

React 🔴 Senior

Architecture interne de React (depuis v16) qui découpe le rendu en unités de travail interruptibles. Permet le rendu concurrent et la priorisation des mises a jour.

Comme un chef cuisinier qui peut interrompre la préparation d'un plat pour servir un client urgent, puis reprendre ou il en était.

// Fiber permet les priorites
// Haute priorite: saisie utilisateur
// Basse priorite: mise a jour d'une liste
const [query, setQuery] = useState('');
const [results, setResults] = useTransition();

Cas d'usage : Fondation du concurrent rendering, permet a React de rester réactif même avec des rendus lourds.

#core#interview#architecture

React Hook Form

React 🟡 Mid? entretien

Librairie de formulaires performante basée sur les refs (uncontrolled). Minimise les re-rendus et offre une validation intégrée avec support de Zod, Yup, etc.

Comme un formulaire papier : tu remplis tout d'un coup et la validation se fait a la soumission, pas a chaque lettre.

const { register, handleSubmit, formState: { errors } } = useForm();

<form onSubmit={handleSubmit(onSubmit)}>
  <input {...register('email', { required: true })} />
  {errors.email && <span>Email requis</span>}
  <button type="submit">Envoyer</button>
</form>

Cas d'usage : Formulaires complexes avec beaucoup de champs ou la performance est critique.

Anti-pattern : Re-implémenter la validation manuellement avec useState au lieu d'utiliser les resolvers integres.
#forms#interview#ecosystem

Depuis React 19.2 (1er octobre 2025), React ajoute ses propres pistes dans l'onglet Performance de Chrome DevTools : une piste Scheduler (priorité des travaux : blocking, transition, idle) et une piste Components (durée de rendu et d'effets par composant). Cela complète le Profiler React en situant le travail React dans la chronologie globale du navigateur (layout, peinture, réseau).

Ajouter les voies « React » sur le plan du métro : on voit enfin où le train React s'arrête par rapport aux autres lignes.

# 1. Application en mode développement (ou build avec profiling)
npm run dev
# 2. Chrome DevTools > Performance > Enregistrer > interagir > Stop
# 3. Pistes « Scheduler ⚛ » et « Components ⚛ » :
#    - Scheduler : Blocking / Transition / Idle, mises à jour et leur cause
#    - Components : Mount, Update, Effects par composant (durée, cascade)
# Astuce : nommer les transitions avec addTransitionType pour les retrouver

Cas d'usage : Diagnostiquer un rendu long ou une cascade d'effets, vérifier qu'une mise à jour lourde est bien en priorité Transition.

Anti-pattern : Profiler uniquement avec le Profiler React sans regarder la piste principale : on rate le layout thrashing ou le JS tiers qui bloque le thread.
#debugging#performance#react19#tooling

React Router — Framework Mode

React 🟡 Mid? entretien

Mode le plus complet de React Router, hérité de Remix lors de la fusion dans la v7 (22 novembre 2024) et pleinement stable en v8 (17 juin 2026, dernière 8.4.0 le 15 septembre 2026). Il ajoute au mode data l'intégration Vite : routes.ts, modules de route typés (Route.LoaderArgs, Route.ComponentProps, href typé), découpage automatique, SSR, pré-rendu statique ou SPA. Le support des Server Components y reste expérimental (RSCHydratedRouter, routeRSCServerRequest, via @vitejs/plugin-rsc).

Le même vélo (le routeur), mais livré avec cadre, freins et éclairage montés : prêt à rouler sur route ouverte.

// app/routes.ts
import { index, route } from '@react-router/dev/routes';
export default [index('./home.tsx'), route('products/:pid', './product.tsx')];

// app/product.tsx — module de route
import type { Route } from './+types/product';
export async function loader({ params }: Route.LoaderArgs) {
  return { product: await getProduct(params.pid) };
}
export default function Product({ loaderData }: Route.ComponentProps) {
  return <h1>{loaderData.product.name}</h1>;
}

Cas d'usage : Application full-stack React sans Next.js, déployable sur Node, Docker ou Cloudflare Workers ; migration d'un projet Remix v2.

Anti-pattern : Mélanger les modes (BrowserRouter déclaratif dans un projet framework) ou mettre en production les API RSC marquées expérimentales sans suivre les notes de version.
#routing#framework#fullstack#interview

React Router v6

React 🟡 Mid? entretien

Librairie de routage de référence pour React. La v6 (routes imbriquées, loaders, actions) reste maintenue (6.30.6 le 18 août 2026), mais la v7 (22 novembre 2024) a fusionné Remix dans React Router et ajouté le « framework mode » ; la v8 (17 juin 2026, dernière 8.4.0 le 15 septembre 2026) est ESM uniquement, supprime react-router-dom au profit du seul paquet react-router, et exige React 19.2.7+.

Comme le plan d'un centre commercial avec les flèches directionnelles : chaque URL mène a la bonne boutique (composant).

// v7/v8 : un seul paquet, `Component` remplace `element`
import { createBrowserRouter, RouterProvider } from 'react-router';

const router = createBrowserRouter([
  {
    path: '/',
    Component: Layout,
    children: [
      { index: true, Component: Home },
      { path: 'users/:id', Component: User, loader: userLoader },
    ],
  },
]);
createRoot(root).render(<RouterProvider router={router} />);

Cas d'usage : SPA Vite + React en mode déclaratif ou data ; application full-stack (SSR, pré-rendu, typage des routes) en mode framework, alternative directe à Next.js.

Anti-pattern : Démarrer un projet neuf en v6 ou avec react-router-dom : ce paquet n'existe plus en v8, tout s'importe depuis react-router.
#routing#interview#ecosystem

React Server Components

React 🔴 Senior

Composants exécutés uniquement sur le serveur (accès direct BDD, fichiers, secrets), dont le code n'est jamais envoyé au navigateur. Ils sont stables dans React 19 (5 décembre 2024) mais nécessitent un framework ou bundler (Next.js, React Router, Parcel, Vite via @vitejs/plugin-rsc) : les API internes de bundling ne suivent pas semver entre versions mineures. Depuis React 19.3 (9 septembre 2026), un <Context value> peut être rendu directement dans un Server Component.

Comme la cuisine d'un restaurant : le client ne voit que le plat (HTML), pas les ustensiles (code serveur).

// Server Component (par defaut dans Next.js App Router)
export default async function UserList() {
  const users = await db.query('SELECT * FROM users');
  return (
    <ul>
      {users.map(u => <li key={u.id}>{u.name}</li>)}
    </ul>
  );
}

Cas d'usage : Composants qui affichent des données sans interactivité : listes, articles, dashboards de lecture.

Anti-pattern : Utiliser useState ou onClick dans un Server Component (il faut 'use client'), ou passer à un Client Component une prop non sérialisable (fonction ordinaire, instance de classe).
#rendering#interview#react19#nextjs

React Testing Library

React 🟡 Mid? entretien

Librairie de test qui encourage les tests du point de vue de l'utilisateur. On interagit via roles, labels et texte visible, pas via les détails d'implémentation.

Comme un testeur mystère dans un restaurant : il teste l'expérience client, pas la recette du chef.

import { render, screen, fireEvent } from '@testing-library/react';

test('incremente le compteur', () => {
  render(<Counter />);
  fireEvent.click(screen.getByRole('button', { name: /incrementer/i }));
  expect(screen.getByText('1')).toBeInTheDocument();
});

Cas d'usage : Tests unitaires et d'intégration de composants React, focus sur le comportement utilisateur.

Anti-pattern : Tester les détails d'implémentation (state interne, nombre de re-rendus) au lieu du comportement.
#testing#interview#bestpractice

React.memo

React 🟡 Mid? entretien

HOC qui memoize un composant : il ne se re-rend que si ses props changent (comparaison shallow par défaut).

Comme un videur de boite de nuit qui ne laisse passer que les nouvelles têtes : mêmes props = on ne re-rend pas.

const ExpensiveList = React.memo(function List({ items }) {
  return items.map(i => <Item key={i.id} {...i} />);
});

// Avec comparateur custom
const MemoComp = React.memo(Comp, (prev, next) => {
  return prev.id === next.id;
});

Cas d'usage : Composant enfant coûteux re-rendu inutilement à cause du parent. Depuis React Compiler 1.0 (7 octobre 2025), la mémoïsation est automatique dans les projets compilés : React.memo devient une soupape manuelle, utile surtout hors compilateur ou pour un contrôle explicite.

Anti-pattern : Envelopper tous les composants sans mesurer : le coût de comparaison peut dépasser le gain, et le compilateur rend l'exercice redondant.
#performance#interview

Reconciliation Algorithm

React 🟡 Mid? entretien

Algorithme de diffing qui compare deux arbres Virtual DOM pour déterminer les changements minimaux a appliquer au vrai DOM. Complexité O(n) grâce a des heuristiques.

Comme un jeu des 7 différences : React compare les deux images et ne corrige que les pixels différents.

// React compare par type d'element et key
// Si le type change: detruit et recree le sous-arbre
// Si le type est identique: met a jour les props
<ul>
  <li key="a">A</li> {/* key aide la reconciliation */}
  <li key="b">B</li>
</ul>

Cas d'usage : Comprendre la réconciliation aide a écrire du code performant et a bien utiliser les keys.

Anti-pattern : Changer le type d'un composant parent inutilement, forçant React a détruire tout le sous-arbre.
#core#interview#performance

Redux Toolkit

React 🟡 Mid? entretien

Boite a outils officielle de Redux qui simplifie la configuration du store, la création de reducers et la gestion de la logique asynchrone.

Comme un kit de montage IKEA pour Redux : tout est pre-configure, plus besoin de 200 fichiers boilerplate.

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    add: (state, action) => { state.value += action.payload; }
  }
});

Cas d'usage : État global complexe nécessitant de la predictibilite, du time-travel debugging et du middleware.

Anti-pattern : Mettre TOUT l'état dans Redux y compris le state UI local (open/close modal).
#state#interview#ecosystem

ref as a prop (fin de forwardRef)

React 🟢 Junior? entretien

Depuis React 19 (5 décembre 2024), un composant fonction reçoit ref comme une prop ordinaire : plus besoin de forwardRef, que la doc officielle annonce déprécié dans une version future. Un codemod est fourni. En complément, un callback ref peut retourner une fonction de nettoyage appelée au démontage.

Avant, il fallait passer par un guichet spécial (forwardRef) pour transmettre la clé ; maintenant la clé arrive dans le même courrier que le reste.

// Avant (React 18)
const Input = forwardRef((props, ref) => <input {...props} ref={ref} />);

// Après (React 19)
function Input({ ref, ...props }) {
  return <input {...props} ref={ref} />;
}

// Callback ref avec nettoyage (React 19)
<div ref={(node) => {
  const obs = new ResizeObserver(onResize);
  if (node) obs.observe(node);
  return () => obs.disconnect();   // appelé au démontage
}} />

Cas d'usage : Composants de design system (Input, Button) qui exposent leur nœud DOM au parent ; abonnements DOM avec nettoyage sans useEffect.

Anti-pattern : Continuer à écrire forwardRef dans du code React 19 neuf, ou retourner implicitement une valeur depuis un callback ref (TypeScript le refuse désormais).
#core#react19#interview

Remix Conventions

React 🟡 Mid

Conventions issues de Remix : routes = fichiers, loader, action et composant co-localisés, standards web (fetch, FormData, Response). Depuis React Router v7 (22 novembre 2024), Remix a été fusionné dans React Router : ces conventions vivent désormais dans le « framework mode » de React Router, stable et par défaut en v8 (17 juin 2026).

Comme un restaurant local bio : il utilise les ingrédients natifs (standards web) au lieu d'importer des produits exotiques.

// app/routes.ts : configuration explicite des routes (v7+)
import { index, route } from '@react-router/dev/routes';
export default [
  index('./home.tsx'),
  route('users/:id', './user.tsx'),
];

// app/user.tsx : loader + action + composant co-localisés
export async function loader({ params }: Route.LoaderArgs) {
  return { user: await getUser(params.id) };
}
export default function User({ loaderData }: Route.ComponentProps) {
  return <h1>{loaderData.user.name}</h1>;
}

Cas d'usage : Migrer une application Remix v2 vers React Router v7/v8 (framework mode) ; démarrer un projet full-stack React sans Next.js.

Anti-pattern : Chercher à installer « Remix » pour un nouveau projet : le framework recommandé est React Router en mode framework.
#framework#fullstack#routing

Render Props

React 🟡 Mid? entretien

Pattern ou un composant reçoit une fonction en prop qui retourne du JSX, permettant de partager de la logique tout en laissant le contrôle du rendu a l'appelant.

Comme un cours de peinture 'apporte ta toile' : le professeur (composant) guide, mais tu décides du résultat.

function MouseTracker({ render }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  return (
    <div onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>
      {render(pos)}
    </div>
  );
}
<MouseTracker render={({ x, y }) => <p>{x}, {y}</p>} />

Cas d'usage : Partager de la logique réutilisable entre composants (souvent remplace par les custom hooks aujourd'hui).

Anti-pattern : Imbriquer plusieurs render props créant un 'callback hell' illisible.
#patterns#interview

Route Groups

React 🟡 Mid

Dossiers parenthèses (group) dans App Router qui organisent les routes sans affecter l'URL. Permettent des layouts différents pour des sections de l'app.

Comme des dossiers de classement invisibles : ils organisent les papiers mais ne changent pas l'adresse postale.

// app/(marketing)/about/page.tsx  -> /about
// app/(marketing)/layout.tsx      -> layout marketing
// app/(dashboard)/admin/page.tsx  -> /admin
// app/(dashboard)/layout.tsx      -> layout dashboard

Cas d'usage : Séparer les layouts (public vs dashboard) ou organiser le code sans impacter les URLs.

#nextjs#routing

Format streamé que le serveur envoie au client pour décrire l'arbre rendu par les Server Components : HTML rendu, références aux Client Components à charger, props sérialisées et Promises encore en cours. Seules les valeurs sérialisables traversent la frontière : primitives, objets simples, Array/Map/Set/Date, Promises, éléments JSX et Server Functions. Fonctions ordinaires, instances de classes et symboles locaux sont refusés. 'use client' marque une frontière de module, pas de composant.

Une notice de montage IKEA : le meuble (HTML) déjà assemblé, plus la liste des pièces à visser sur place (Client Components) et leurs vis (props).

// Server Component
export default async function Page() {
  const user = await db.users.get(1);          // objet simple : OK
  const postsPromise = db.posts.byUser(1);      // Promise : OK, streamée
  return (
    <Profile
      user={user}
      posts={postsPromise}
      onSave={saveProfile}   // Server Function ('use server') : OK
      // formatter={new Intl.DateTimeFormat()}  ❌ instance de classe
    />
  );
}

Cas d'usage : Comprendre pourquoi une prop « ne passe pas » à un Client Component, et pourquoi le code serveur (BDD, secrets) n'atteint jamais le navigateur.

Anti-pattern : Passer un callback client ordinaire ou un objet de classe depuis un Server Component ; ou ajouter 'use client' partout, ce qui déplace toute la page dans le bundle.
#rsc#react19#interview#architecture

RTK Query

React 🟡 Mid

Solution de data fetching et caching intégrée a Redux Toolkit. Génère automatiquement les hooks, gère le cache, l'invalidation et les requêtes optimistes.

Comme un assistant personnel qui passe les commandes, gère le stock et prévient quand il faut réapprovisionner.

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (build) => ({
    getUsers: build.query({ query: () => '/users' }),
    addUser: build.mutation({ query: (body) => ({ url: '/users', method: 'POST', body }) })
  })
});

Cas d'usage : Applications Redux avec beaucoup de data fetching, en alternative a React Query dans l'écosystème Redux.

#state#data-fetching

Server Actions

React 🟡 Mid

Fonctions asynchrones exécutées sur le serveur et appelables depuis un composant client, typiquement via la prop action d'un <form>. Depuis septembre 2024, React les appelle « Server Functions » ; « Server Action » désigne le cas particulier d'une Server Function passée à une prop action. Stables dans React 19 (5 décembre 2024).

Comme commander par téléphone : tu passes l'ordre (client) et la cuisine (serveur) exécuté, sans aller au comptoir (API route).

// actions.ts
'use server';
import { updateTag } from 'next/cache';
export async function addTodo(formData: FormData) {
  const session = await getSession();          // autorisation ICI
  if (!session) throw new Error('Non autorisé');
  await db.todos.create({ text: String(formData.get('text')) });
  updateTag('todos');                          // read-your-writes
}
// component.tsx
<form action={addTodo}><input name="text" /><button>Ajouter</button></form>

Cas d'usage : Soumission de formulaire, CRUD, toute mutation serveur, avec revalidation du cache (Next.js 16 : updateTag pour relire immédiatement ses propres écritures, refresh pour les données non mises en cache).

Anti-pattern : Oublier que chaque Server Function est un point d'entrée HTTP public : valider les entrées et vérifier l'autorisation dans la fonction elle-même. Le 3 décembre 2025, la CVE-2025-55182 (RCE, CVSS 10.0) touchait la désérialisation de ces requêtes dans react-server-dom-* 19.0 à 19.2.0 ; correctifs 19.0.1, 19.1.2, 19.2.1.
#rendering#react19#nextjs#security

Server Functions ('use server')

React 🟡 Mid? entretien

Fonctions asynchrones marquées 'use server' qui s'exécutent sur le serveur et dont le client reçoit une référence sérialisée. Nom officiel depuis septembre 2024 ; « Server Action » désigne le cas où la fonction est passée à une prop action. Appelables via <form action>, useActionState ou startTransition. Stables dans React 19 (5 décembre 2024) ; les arguments et retours doivent être sérialisables.

Un bouton d'appel d'ascenseur : vous appuyez côté client, la machinerie côté serveur fait le travail, vous n'avez que le bouton.

// actions.js
'use server';
export async function createNote(title) {
  await requireUser();               // autorisation obligatoire
  return db.notes.create({ title }); // retour sérialisable
}

// NoteForm.jsx
'use client';
import { createNote } from './actions';
function NoteForm() {
  const [isPending, startTransition] = useTransition();
  const save = () => startTransition(async () => { await createNote('Idée'); });
  return <button onClick={save} disabled={isPending}>Créer</button>;
}

Cas d'usage : Mutations sans route API manuelle : formulaires, boutons d'action, appels RPC typés entre client et serveur.

Anti-pattern : Traiter une Server Function comme privée : c'est un endpoint HTTP exposé, à protéger comme une route API (validation, auth, dépendances à jour après la CVE-2025-55182).
#rsc#react19#interview

Server-side Validation

React 🟡 Mid

Validation des données cote serveur en complément du client, car la validation client est contournable. Avec Server Actions, le même schema Zod peut valider des deux cotes.

Comme un double contrôle de sécurité : le portique (client) puis la fouille (serveur). Le portique seul ne suffit pas.

'use server';
const schema = z.object({ email: z.string().email() });

export async function createUser(formData: FormData) {
  const result = schema.safeParse(Object.fromEntries(formData));
  if (!result.success) return { errors: result.error.flatten() };
  await db.users.create(result.data);
}

Cas d'usage : Toute mutation de données : la validation client est UX, la validation serveur est sécurité.

Anti-pattern : Se reposer uniquement sur la validation client, exposant l'app a des données malformées.
#forms#security#bestpractice

Signals

React 🔴 Senior

Primitives reactives a grain fin qui mettent a jour le DOM directement sans re-rendu de composant. Popularises par Preact/SolidJS, en discussion pour React.

Comme un tableau d'affichage électronique : seule la case qui change se met a jour, pas tout le panneau.

// Avec @preact/signals-react
import { signal, computed } from '@preact/signals-react';

const count = signal(0);
const double = computed(() => count.value * 2);

function App() {
  return <p>{count} x 2 = {double}</p>;
}

Cas d'usage : Performance maximale avec des mises a jour granulaires sans réconciliation de tout le sous-arbre.

#state#advanced#future

Slot Pattern

React 🟡 Mid

Pattern qui permet au parent de passer du contenu JSX dans des emplacements nommes du composant enfant, via des props ou children spécifiques.

Comme un formulaire administratif avec des cases a remplir : chaque case (slot) attend un contenu spécifique.

function Layout({ header, sidebar, children }) {
  return (
    <div className="layout">
      <header>{header}</header>
      <aside>{sidebar}</aside>
      <main>{children}</main>
    </div>
  );
}
<Layout header={<Nav />} sidebar={<Menu />}>Contenu</Layout>

Cas d'usage : Composants de layout configurables avec plusieurs zones de contenu (header, footer, sidebar).

#patterns#composition

SSG (Static Site Generation)

React 🟡 Mid? entretien

Les pages HTML sont générées au moment du build, pas a la requête. Résultat ultra-rapide puisque le CDN sert des fichiers statiques pre-générés.

Comme imprimer des flyers a l'avance : pas besoin de les créer a la demande, ils sont déjà prêts a distribuer.

// Next.js - page statique par defaut dans App Router
export default async function Blog() {
  const posts = await getPosts(); // execute au build
  return posts.map(p => <article key={p.id}>{p.title}</article>);
}
// export const dynamic = 'force-static';

Cas d'usage : Blogs, documentation, pages marketing dont le contenu ne change pas a chaque requête.

Anti-pattern : Utiliser SSG pour du contenu très dynamique nécessitant des rebuilds fréquents.
#rendering#interview#performance

SSR (Server-Side Rendering)

React 🟡 Mid? entretien

Le serveur génère le HTML complet a chaque requête. Le navigateur affiche la page immédiatement puis l'hydraté avec React pour la rendre interactive.

Comme un plat prépare au restaurant (serveur) vs un kit a assembler chez soi (CSR) : le client reçoit un plat déjà prêt.

// Next.js App Router
export default async function Page() {
  const data = await fetch('https://api.example.com/data');
  const posts = await data.json();
  return <PostList posts={posts} />;
}

Cas d'usage : Pages publiques nécessitant un bon SEO et un premier affichage rapide (e-commerce, blogs, landing pages).

Anti-pattern : Faire du SSR pour des pages privées derrière authentification ou le SEO est inutile.
#rendering#interview#architecture

State

React 🟢 Junior? entretien

Données internes et mutables d'un composant. Quand le state change, React re-rend le composant pour refléter la nouvelle valeur.

Comme le score dans un jeu vidéo : il change au fil du temps et l'affichage se met a jour automatiquement.

const [count, setCount] = useState(0);
return (
  <button onClick={() => setCount(c => c + 1)}>
    Clics: {count}
  </button>
);

Cas d'usage : Stocker des données dynamiques propres a un composant (formulaires, toggles, compteurs).

Anti-pattern : Muter le state directement (state.push()) au lieu d'utiliser le setter avec une nouvelle référence.
#core#interview#fundamentals

State Reducer Pattern

React 🔴 Senior

Pattern qui permet au consommateur d'un composant de personnaliser les transitions d'état en fournissant son propre reducer, fusionnant avec le comportement par défaut.

Comme un pilote automatique avec override : l'avion vole seul mais le pilote peut reprendre le contrôle a tout moment.

function useToggle({ reducer = defaultReducer } = {}) {
  const [state, dispatch] = useReducer(reducer, { on: false });
  const toggle = () => dispatch({ type: 'TOGGLE' });
  return { ...state, toggle };
}
// L'utilisateur peut injecter son reducer custom
const { on, toggle } = useToggle({
  reducer: (state, action) => action.type === 'TOGGLE' && tooMany ? state : defaultReducer(state, action)
});

Cas d'usage : Librairies/composants génériques ou l'utilisateur doit pouvoir overrider le comportement interne.

#patterns#advanced

Storybook

React 🟡 Mid

Outil de développement et documentation de composants UI en isolation. Chaque 'story' montre un état du composant avec ses variantes.

Comme un catalogue IKEA : chaque meuble (composant) est photographie dans tous ses coloris et configurations.

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';

const meta: Meta<typeof Button> = { component: Button };
export default meta;

export const Primary: StoryObj = { args: { variant: 'primary', children: 'Click' } };
export const Disabled: StoryObj = { args: { disabled: true, children: 'Off' } };

Cas d'usage : Développer, tester visuellement et documenter les composants UI d'un design system.

#testing#documentation#design-system

Streaming SSR

React 🔴 Senior

Le serveur envoie le HTML en flux continu au lieu d'attendre la page entière ; les parties lentes (sous Suspense) arrivent au fur et à mesure. Depuis React 19.2 (1er octobre 2025), React regroupe la révélation des frontières Suspense proches dans le temps (Suspense batching) pour éviter les affichages saccadés, et les API Web Streams (renderToReadableStream, prerender, résume) fonctionnent aussi sous Node.js.

Comme regarder un match en streaming : tu vois l'action en direct au lieu d'attendre que l'enregistrement soit complet.

// Next.js App Router avec Suspense
export default function Page() {
  return (
    <main>
      <Header /> {/* envoye immediatement */}
      <Suspense fallback={<Skeleton />}>
        <SlowDataSection /> {/* stream quand pret */}
      </Suspense>
    </main>
  );
}

Cas d'usage : Pages avec des sections de vitesses différentes : le header s'affiche vite, les données arrivent en stream.

#rendering#performance#advanced

StrictMode

React 🟢 Junior

Composant de développement qui active des vérifications supplémentaires et double les rendus et effets pour révéler les effets de bord impurs. Depuis React 19 (5 décembre 2024), useMemo et useCallback réutilisent leur résultat du premier rendu lors du double rendu ; React 19.3 (9 septembre 2026) double aussi l'invocation des effets pendant l'hydratation.

Comme un professeur qui fait refaire l'exercice deux fois pour vérifier que le résultat est toujours le même.

<React.StrictMode>
  <App />
</React.StrictMode>
// En dev, useEffect s'execute 2 fois pour detecter les bugs

Cas d'usage : Activer en développement pour détecter les effets de bord, les API dépréciées et les problèmes de cleanup.

#core#debugging#react19

React 19 (5 décembre 2024, guide de migration publié le 25 avril 2024) supprime les API dépréciées de longue date : propTypes et defaultProps sur les composants fonction (utiliser TypeScript et les paramètrès par défaut), les refs chaînés, le legacy Context, React.createFactory, ReactDOM.render, hydraté, unmountComponentAtNode, findDOMNode et react-test-renderer/shallow. Les builds UMD disparaissent aussi. Le codemod react/19/migration-recipe automatise l'essentiel.

Un grand déménagement : ce qui était étiqueté « à jeter » depuis des années part enfin à la benne.

// ❌ React 18
function Card({ title }) { /* ... */ }
Card.propTypes = { title: PropTypes.string };
Card.defaultProps = { title: 'Sans titre' };
ReactDOM.render(<App />, document.getElementById('root'));

// ✅ React 19
function Card({ title = 'Sans titre' }: { title?: string }) { /* ... */ }
createRoot(document.getElementById('root')).render(<App />);

// Migration automatisée
// npx codemod@latest react/19/migration-recipe
// npx types-react-codemod@latest preset-19 ./src

Cas d'usage : Question d'entretien classique : « qu'est-ce qui casse en passant à React 19 ? » ; audit d'une base legacy avant montée de version.

Anti-pattern : Présenter PropTypes ou defaultProps comme la façon de typer un composant : ils sont ignorés (voire supprimés) depuis React 19.
#core#react19#migration#interview

Suspense

React 🟡 Mid? entretien

Composant qui affiche un fallback pendant que ses enfants chargent des données ou du code asynchrone. Pilier du rendu concurrent.

Comme un écran 'Chargement...' au cinéma pendant que le projectionniste change de bobine.

const LazyPage = React.lazy(() => import('./Page'));

<Suspense fallback={<Spinner />}>
  <LazyPage />
</Suspense>

Cas d'usage : Afficher un loader pendant le chargement paresseux de composants ou de données avec React Query.

Anti-pattern : Entourer chaque composant avec Suspense individuellement au lieu de regrouper les limites logiques.
#core#interview#performance

SWR

React 🟡 Mid

Librairie de fetching de Vercel basée sur la stratégie stale-while-revalidate : affiche les données en cache immédiatement, puis revalide en arrière-plan.

Comme un journal du matin : tu lis celui d'hier (cache) en attendant que le facteur apporte le nouveau (revalidation).

import useSWR from 'swr';

const fetcher = (url) => fetch(url).then(r => r.json());

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher);
  if (isLoading) return <Spinner />;
  return <p>{data.name}</p>;
}

Cas d'usage : Alternative plus légère a TanStack Query pour des cas de fetching simples.

#data-fetching#ecosystem

TanStack Query

React 🟡 Mid? entretien

Librairie de gestion de l'état serveur (fetching, caching, synchronisation). Gère automatiquement le cache, la revalidation, le retry et les mises a jour optimistes.

Comme un concierge d'hôtel : il anticipe tes besoins, garde tes affaires en cache et les rafraîchit discrètement.

const { data, isLoading } = useQuery({
  queryKey: ['users', userId],
  queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
  staleTime: 5 * 60 * 1000,
  gcTime: 10 * 60 * 1000,
});

Cas d'usage : Toute application qui consomme une API REST/GraphQL et a besoin de cache intelligent et de revalidation.

Anti-pattern : Stocker le résultat de useQuery dans un useState local, duplicant l'état et perdant le cache.
#data-fetching#interview#ecosystem

TanStack Start

React 🔴 Senior

Framework full-stack bâti sur TanStack Router (routage typé) qui ajoute SSR, streaming, server functions et server routes avec middleware composable, sur Vite ou Rsbuild. Toujours en Release Candidate au 27 septembre 2026 (paquet @tanstack/react-start 1.168.58 publié le 23 septembre 2026), déployable sur Netlify, Vercel, Cloudflare, Node. Les server functions se déclarent avec createServerFn().validator().handler() et se consomment via useServerFn ou les loaders.

Un kit de construction où chaque pièce est étiquetée et typée : impossible d'assembler un loader avec la mauvaise route.

import { createServerFn } from '@tanstack/react-start';
import { z } from 'zod';

export const createUser = createServerFn({ method: 'POST' })
  .validator(z.object({ name: z.string().min(1) }))
  .handler(async ({ data }) => {
    return db.users.insert({ name: data.name }); // serveur uniquement
  });

// Route : préchargement typé
export const Route = createFileRoute('/users')({
  loader: () => getUsers(),
});
// Composant : const create = useServerFn(createUser);

Cas d'usage : Équipes déjà sur TanStack Query/Router qui veulent un full-stack très typé sans adopter Next.js ni les RSC.

Anti-pattern : Importer un module .server.ts (accès BDD) depuis un composant client : seuls les wrappers createServerFn sont sûrs à importer côté client.
#framework#fullstack#ecosystem

Turbopack

React 🟡 Mid

Bundler incrémental écrit en Rust, intégré à Next.js. Stable pour next dev depuis Next.js 15 et bundler par défaut pour dev et build depuis Next.js 16 (21 octobre 2025), avec 2 à 5× de gain annoncé au build et jusqu'à 10× sur le Fast Refresh. Next.js 16.3 (3 août 2026) active par défaut le cache disque en dev et en build (jusqu'à 90 % de mémoire en moins en dev) et ajoute import.meta.glob. Les plugins webpack ne sont pas supportés ; retour possible avec --webpack.

Un chef qui ne recuit que l'ingrédient modifié au lieu de refaire tout le plat à chaque retouche.

// package.json — Turbopack est le défaut, rien à configurer
{ "scripts": { "dev": "next dev", "build": "next build" } }
// Retour à webpack si un plugin l'exige : next build --webpack

// next.config.ts — options Turbopack
export default {
  turbopack: {
    resolveAlias: { underscore: 'lodash' },
    resolveExtensions: ['.mdx', '.tsx', '.ts', '.jsx', '.js', '.json'],
  },
};
// Vite-compatible : const posts = import.meta.glob('./posts/*.md', { eager: true });

Cas d'usage : Tout projet Next.js 16+ ; gros monorepos où le temps de compilation et la mémoire du dev server étaient le goulot.

Anti-pattern : Garder une config webpack() dans next.config.js et s'étonner qu'elle soit ignorée : Turbopack a sa propre clé turbopack.
#nextjs#tooling#performance

use() Hook

React 🟡 Mid? entretien

API stable depuis React 19 (5 décembre 2024) qui lit une Promise ou un Context directement pendant le rendu. Contrairement aux hooks classiques, use() peut être appelée dans une condition ou une boucle. Sur une Promise, le composant suspend jusqu'à la résolution (Suspense) ; sur un Context, elle remplace useContext.

Comme un lecteur universel : qu'on lui donne un CD (Promise) ou une clé USB (Context), il lit les deux.

// Server Component : on passe la Promise sans l'attendre
export default function Page() {
  const commentsPromise = db.comments.list();
  return (
    <Suspense fallback={<Spinner />}>
      <Comments promise={commentsPromise} />
    </Suspense>
  );
}
// Client Component
'use client';
function Comments({ promise }) {
  const comments = use(promise);      // suspend jusqu'à résolution
  const theme = use(ThemeContext);    // remplace useContext
  return comments.map(c => <p key={c.id}>{c.text}</p>);
}

Cas d'usage : Consommer côté client une Promise créée par un Server Component (streaming des données), ou lire un Context sous condition. En React 19.3 (9 septembre 2026), use(browser()) permet aussi de rendre un composant uniquement dans le navigateur.

Anti-pattern : Créer la Promise dans le rendu du composant client (nouvelle Promise à chaque rendu = boucle de suspension) : elle doit venir d'un Server Component, d'un cache ou d'un loader.
#hooks#react19#interview

useActionState

React 🟡 Mid? entretien

Hook stable de React 19 (5 décembre 2024) qui remplace l'ancien useFormState. Il enveloppe une Action et retourne [state, dispatch, isPending] : l'Action reçoit l'état précédent et le payload (souvent un FormData), et son retour devient le nouvel état. Un troisième argument permalink permet la soumission avant hydratation (progressive enhancement).

Un formulaire papier qu'on renvoie avec, agrafé dessus, le dernier accusé de réception : on sait toujours où l'on en est.

'use client';
import { useActionState } from 'react';
import { updateName } from './actions'; // Server Function

function Form() {
  const [state, submitAction, isPending] = useActionState(
    async (prev, formData) => ({ error: await updateName(formData.get('name')) ?? null }),
    { error: null }
  );
  return <form action={submitAction}>
    <input name="name" />
    <button disabled={isPending}>Valider</button>
    {state.error && <p>{state.error}</p>}
  </form>;
}

Cas d'usage : Formulaire avec erreurs de validation renvoyées par le serveur et bouton désactivé pendant l'envoi, sans état local manuel.

Anti-pattern : Appeler dispatch hors d'une transition ou d'une prop action (React l'exige), ou passer un état initial non sérialisable avec une Server Function.
#hooks#react19#forms#interview

useCallback

React 🟡 Mid? entretien

Hook qui memoize une fonction pour conserver la même référence entre les rendus. Équivalent a useMemo(() => fn, deps).

Comme donner toujours le même numéro de téléphone a un contact : il n'a pas besoin de mettre a jour son carnet a chaque fois.

const handleClick = useCallback((id) => {
  setItems(prev => prev.filter(i => i.id !== id));
}, []); // reference stable

<MemoizedList onDelete={handleClick} />

Cas d'usage : Stabiliser la référence d'un callback passé à un enfant mémoïsé. Avec React Compiler 1.0 (7 octobre 2025), le compilateur produit ces références stables lui-même ; useCallback manuel sert de soupape quand on veut garder la main.

Anti-pattern : useCallback sans React.memo (ou sans compilateur) sur l'enfant : la mémoïsation ne sert à rien ; et depuis le compilateur, l'ajouter partout alourdit le code sans gain.
#hooks#interview#performance

useContext

React 🟢 Junior

Hook qui lit la valeur du Context le plus proche dans l'arbre. Depuis React 19 (5 décembre 2024), use(Context) fait la même chose mais peut être appelé sous condition ; useContext reste valide et n'est pas déprécié.

Comme la radio FM : tout le monde dans la zone capte le même signal sans câble direct.

const ThemeCtx = createContext('light');

function App() {
  return (
    <ThemeCtx value="dark">      {/* React 19 : plus de .Provider */}
      <Toolbar />
    </ThemeCtx>
  );
}
function Button() {
  const theme = useContext(ThemeCtx);   // ou use(ThemeCtx)
  return <button className={theme} />;
}

Cas d'usage : Partager des données globales (theme, langue, user) sans passer par chaque niveau de props.

Anti-pattern : Mettre trop de données dans un seul contexte, causant des re-rendus inutiles de tous les consommateurs.
#hooks#interview#state

useDeferredValue

React 🟡 Mid

Hook qui retourne une version différée d'une valeur, permettant a React de prioriser les mises a jour urgentes et de différer le re-rendu du contenu non critique.

Comme un écho : ta voix (valeur originale) arrive d'abord, puis l'écho (valeur différée) suit avec un léger décalage.

const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);

// L'input reste reactif, la liste se met a jour en differe
<input value={query} onChange={e => setQuery(e.target.value)} />
<HeavyList filter={deferredQuery} />

Cas d'usage : Alternative a useTransition quand tu ne contrôles pas le setter de l'état (props venant du parent).

Anti-pattern : Utiliser sur des valeurs primitives simples qui ne causent pas de rendu lourd.
#hooks#performance#react18

useEffect

React 🟢 Junior

Hook pour synchroniser un composant avec un système externe (API, DOM, timer). S'exécute après le rendu, avec un tableau de dépendances pour contrôler quand il se relance.

Comme un abonnement a un journal : tu t'abonnes (setup), tu reçois les numéros (re-runs), et tu te desabonnes (cleanup) en partant.

useEffect(() => {
  const ctrl = new AbortController();
  fetch('/api/data', { signal: ctrl.signal })
    .then(r => r.json())
    .then(setData);
  return () => ctrl.abort(); // cleanup
}, [userId]); // re-run si userId change

Cas d'usage : Appels API, abonnements WebSocket, synchronisation avec le DOM ou des librairies externes.

Anti-pattern : Omettre des dépendances pour 'éviter les re-runs', causant des stale closures avec des valeurs périmées.
#hooks#interview#fundamentals

useEffectEvent

React 🟡 Mid? entretien

Hook stable depuis React 19.2 (1er octobre 2025) qui extrait d'un effet la logique « événementielle » non réactive. La fonction retournée lit toujours les dernières props et états sans figurer dans le tableau de dépendances, ce qui évite de resynchroniser l'effet (reconnexion, redémarrage de timer) pour une valeur qui ne devrait pas le déclencher.

Un interphone : la connexion reste ouverte (l'effet), mais le message diffusé (l'événement) est toujours le plus récent.

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification('Connecté !', theme); // lit le theme courant
  });

  useEffect(() => {
    const conn = createConnection(roomId);
    conn.on('connected', () => onConnected());
    conn.connect();
    return () => conn.disconnect();
  }, [roomId]); // ✅ theme absent : changer de thème ne reconnecte pas
}

Cas d'usage : Notifications, analytics ou logs déclenchés depuis un abonnement, un timer ou un WebSocket, dépendant de valeurs qui changent souvent.

Anti-pattern : S'en servir pour « faire taire » exhaustive-deps sur une vraie dépendance, ou passer l'Effect Event à un composant enfant (interdit par le lint).
#hooks#react19#interview

useFormStatus

React 🟡 Mid

Hook qui donne l'état de soumission du formulaire parent (pending, data, method, action). Doit être utilise dans un composant enfant du <form>.

Comme un indicateur lumineux sur une machine a café : il te dit si elle est en train de préparer (pending) ou prête.

function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button disabled={pending}>
      {pending ? 'Envoi...' : 'Envoyer'}
    </button>
  );
}

Cas d'usage : Désactiver le bouton submit et afficher un spinner pendant la soumission d'un formulaire avec Server Actions.

Anti-pattern : Utiliser useFormStatus dans le même composant que le <form> : il doit être dans un enfant.
#hooks#react19#forms

useId

React 🟢 Junior

Hook qui génère un identifiant unique stable entre serveur et client, utile pour les attributs d'accessibilite comme htmlFor et aria-describedby.

Comme un tampon officiel : le même numéro est attribue que tu sois au guichet (serveur) ou en ligne (client).

function Input({ label }) {
  const id = useId();
  return (
    <>
      <label htmlFor={id}>{label}</label>
      <input id={id} />
    </>
  );
}

Cas d'usage : Générer des IDs pour les associations label/input sans risque de collision en SSR.

Anti-pattern : Utiliser useId pour générer des keys de liste : il n'est pas prévu pour ca.
#hooks#a11y#react18

useImperativeHandle

React 🟡 Mid

Hook qui personnalise la valeur exposée par la ref d'un composant, pour n'exposer que quelques méthodes (focus, reset) au parent. Depuis React 19 (5 décembre 2024), la ref arrive comme une prop ordinaire : forwardRef n'est plus nécessaire et sera déprécié.

Comme un interphone d'immeuble : tu choisis quels boutons exposer, pas toutes les clés de l'appartement.

// React 19 : `ref` est une prop, plus besoin de forwardRef
function FancyInput({ ref, ...props }) {
  const inputRef = useRef(null);
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current.focus(),
    clear: () => { inputRef.current.value = ''; },
  }), []);
  return <input ref={inputRef} {...props} />;
}
// Parent
const ref = useRef(null);
<FancyInput ref={ref} />; ref.current.focus();

Cas d'usage : Exposer une API impérative limitée a un composant enfant (focus, scroll, reset).

Anti-pattern : Abuser de ce pattern pour recréer de la communication parent-enfant impérative au lieu du flux déclaratif.
#hooks#advanced#react19

useInsertionEffect

React 🔴 Senior

Hook qui s'exécute avant toute mutation DOM, conçu pour les librairies CSS-in-JS qui doivent injecter des balises <style> avant le layout.

Comme poser le papier peint avant d'installer les meubles : les styles sont prêts avant que le DOM n'existe.

// Reserve aux auteurs de libs CSS-in-JS
useInsertionEffect(() => {
  const style = document.createElement('style');
  style.textContent = '.btn { color: red; }';
  document.head.appendChild(style);
  return () => style.remove();
}, []);

Cas d'usage : Exclusivement réserve aux librairies CSS-in-JS (styled-components, Émotion) pour injecter du CSS.

#hooks#advanced#css

useLayoutEffect

React 🟡 Mid

Identique a useEffect mais s'exécute de manière synchrone après les mutations DOM et avant que le navigateur ne peigne. Bloque le rendu visuel.

Comme repositionner les meubles avant de prendre la photo : tout est en place avant que l'utilisateur ne voie.

useLayoutEffect(() => {
  const { height } = ref.current.getBoundingClientRect();
  setTooltipPos(height + 10);
}, []);

Cas d'usage : Mesurer le DOM (position, taille) ou corriger un layout avant l'affichage pour éviter un flash visuel.

Anti-pattern : Utiliser useLayoutEffect par défaut au lieu de useEffect, bloquant inutilement le rendu.
#hooks#performance#dom

useMemo

React 🟡 Mid? entretien

Hook qui memoize le résultat d'un calcul coûteux et ne le recalcule que si ses dépendances changent.

Comme mettre un plat au frigo : tu ne recuisines pas si les ingrédients n'ont pas change, tu ressers le même plat.

const sortedItems = useMemo(
  () => items.sort((a, b) => a.price - b.price),
  [items]
);
// Ne retrie que si 'items' change

Cas d'usage : Calculs coûteux (tri, filtrage) recalculés à chaque rendu. Avec React Compiler 1.0 (7 octobre 2025), ces mémoïsations sont insérées automatiquement au build : useMemo manuel reste une porte de sortie, plus une obligation.

Anti-pattern : Mémoïser par réflexe des opérations triviales : coût supérieur au recalcul, et redondant avec le compilateur.
#hooks#interview#performance

useOptimistic

React 🟡 Mid

Hook stable depuis React 19 (5 décembre 2024) qui affiche un état optimiste pendant qu'une Action est en cours. Si l'Action échoue, React revient automatiquement à la valeur réelle ; si elle réussit, état optimiste et état réel convergent dans le même rendu, sans rendu supplémentaire.

Comme cocher 'lu' sur un message avant que le serveur confirme : si ca échoué, la coche disparaît.

const [optimisticLikes, addOptimistic] = useOptimistic(
  likes,
  (state, newLike) => [...state, newLike]
);

function handleLike() {
  startTransition(async () => {       // obligatoire : dans une Action
    addOptimistic({ id: 'temp', user: 'moi' });
    await saveLike();                 // échec => retour à `likes`
  });
}

Cas d'usage : Likes, commentaires, toggles : feedback instantané a l'utilisateur pendant la requête serveur.

Anti-pattern : Appeler le setter optimiste hors d'une Action (startTransition ou prop action) : React affiche un avertissement et l'état optimiste ne se comporte pas comme prévu.
#hooks#react19#ux

useReducer

React 🟡 Mid? entretien

Hook alternatif a useState pour gérer un état complexe avec une logique de type action/reducer, inspire de Redux.

Comme un standard téléphonique : tu envoies un message (action) et l'opérateur (reducer) décidé quoi faire.

const reducer = (state, action) => {
  switch(action.type) {
    case 'increment': return { count: state.count + 1 };
    case 'reset': return { count: 0 };
    default: return state;
  }
};
const [state, dispatch] = useReducer(reducer, { count: 0 });

Cas d'usage : États complexes avec plusieurs sous-valeurs interdépendantes ou quand la logique de transition est élaborée.

Anti-pattern : Utiliser useReducer pour un simple toggle booléen : useState est plus simple et lisible.
#hooks#interview#state

useRef

React 🟢 Junior

Hook qui retourne un objet mutable {current} persistant entre les rendus, sans déclencher de re-rendu quand il change. Sert a référencer des éléments DOM ou stocker des valeurs.

Comme un tiroir secret dans un bureau : tu y ranges des choses sans que personne ne le remarque (pas de re-rendu).

const inputRef = useRef(null);
const timerRef = useRef(null);

useEffect(() => {
  inputRef.current.focus();
  timerRef.current = setInterval(tick, 1000);
  return () => clearInterval(timerRef.current);
}, []);

Cas d'usage : Accéder au DOM (focus, scroll), stocker des IDs de timer, garder la valeur précédenté d'un state.

Anti-pattern : Utiliser useRef pour stocker du state qui devrait déclencher un re-rendu (utiliser useState a la place).
#hooks#interview#dom

useState

React 🟢 Junior

Hook qui ajoute un état local a un composant fonctionnel. Retourne la valeur courante et une fonction pour la mettre a jour.

Comme un post-it sur le frigo : tu lis la valeur et tu la remplaces quand elle change.

const [name, setName] = useState('');

// Mise a jour fonctionnelle (recommandee)
setCount(prev => prev + 1);

// Initialisation couteuse avec lazy init
const [data, setData] = useState(() => expensiveCalc());

Cas d'usage : Tout état local simple : champs de formulaire, toggles, compteurs, données UI.

Anti-pattern : Appeler le setter dans le corps du composant sans condition, causant une boucle infinie de re-rendus.
#hooks#interview#fundamentals

useSyncExternalStore

React 🔴 Senior

Hook pour s'abonner a un store externe (non-React) de manière compatible avec le rendu concurrent, évitant les tearing (desynchro visuelle).

Comme un écran d'aéroport synchronise : tous les passagers voient les mêmes horaires, pas de version périmée.

const width = useSyncExternalStore(
  (cb) => { window.addEventListener('resize', cb); return () => window.removeEventListener('resize', cb); },
  () => window.innerWidth,
  () => 1024 // server snapshot
);

Cas d'usage : Intégration avec des stores externes (Zustand, Redux interne) ou des API navigateur (resize, online).

#hooks#advanced#react18

useTransition

React 🟡 Mid? entretien

Hook qui marque une mise à jour comme non urgente : React peut la différer pour garder l'interface réactive. Depuis React 19 (5 décembre 2024), startTransition accepte une fonction async : c'est une « Action », qui gère automatiquement l'état pending, les erreurs et les mises à jour optimistes.

Comme un panneau 'en cours de rénovation' : les clients voient l'ancien décor pendant que tu renoves en arrière-plan.

const [isPending, startTransition] = useTransition();

// React 18 : mise à jour lourde non urgente
startTransition(() => setFiltered(heavyFilter(query)));

// React 19 : Action asynchrone
function handleSave() {
  startTransition(async () => {
    const err = await updateName(name); // isPending = true pendant l'await
    if (err) setError(err);
  });
}

Cas d'usage : Filtrage de longues listes, navigation entre onglets, et depuis React 19 toute mutation asynchrone (soumission, appel serveur) dont on veut l'état isPending sans state manuel.

Anti-pattern : Envelopper des mises a jour urgentes (saisie de texte) dans startTransition, rendant l'input laggy.
#hooks#interview#performance#react19

Valtio

React 🟡 Mid

State management base sur des proxies JavaScript. L'état est mutable directement et Valtio détecte les changements automatiquement via Proxy.

Comme un miroir magique : tu modifies l'objet original et le reflet (composant) se met a jour automatiquement.

const state = proxy({ count: 0, text: 'hello' });

function Counter() {
  const snap = useSnapshot(state);
  return (
    <button onClick={() => { state.count++; }}>
      {snap.count}
    </button>
  );
}

Cas d'usage : Développeurs préférant la mutation directe d'objets, prototypage rapide, migration depuis MobX.

#state#ecosystem

Virtual DOM

React 🟢 Junior

Représentation légère du DOM réel en mémoire. React compare l'ancien et le nouveau Virtual DOM pour ne modifier que ce qui a change.

Comme un brouillon avant d'envoyer un email : tu fais tes corrections sur le brouillon, puis tu envoies seulement la version finale.

// React cree un arbre virtuel
// Ancien: <p>Bonjour</p>
// Nouveau: <p>Salut</p>
// React ne change que le texte du <p>, pas tout le DOM

Cas d'usage : Mécanisme interne qui optimise les mises a jour du DOM sans intervention manuelle.

Anti-pattern : Manipuler le DOM directement avec document.querySelector au lieu de laisser React gérer.
#core#interview#performance

Virtualization

React 🟡 Mid? entretien

Technique de rendu qui n'affiche que les éléments visibles dans le viewport, même pour des listes de milliers d'items. Les éléments hors écran ne sont pas montes.

Comme un ascenseur panoramique : tu ne vois que les étages devant toi, pas les 100 étages en même temps.

import { useVirtualizer } from '@tanstack/react-virtual';

const virtualizer = useVirtualizer({
  count: 10000,
  getScrollElement: () => parentRef.current,
  estimateSize: () => 35,
});
// Ne rend que ~20 items visibles au lieu de 10000

Cas d'usage : Listes ou tableaux de plus de quelques centaines d'éléments (logs, contacts, produits).

Anti-pattern : Virtualiser une liste de 20 éléments : l'overhead de la virtualisation n'en vaut pas la peine.
#performance#interview

Vite + @vitejs/plugin-react

React 🟢 Junior

Vite est l'outil de build recommandé par l'équipe React pour une SPA sans framework depuis la dépréciation de CRA (14 février 2025). Version majeure courante : Vite 8 (12 mars 2026), suivie de Vite 8.1 (23 juin 2026). Le plugin @vitejs/plugin-react fournit Fast Refresh et, via reactCompilerPreset() avec @rolldown/plugin-babel, l'activation de React Compiler. C'est aussi le socle de React Router en mode framework et de TanStack Start.

Une cuisine équipée prête à l'emploi : on branche et on cuisine, sans monter les meubles soi-même.

// vite.config.js
import { defineConfig } from 'vite';
import react, { reactCompilerPreset } from '@vitejs/plugin-react';
import babel from '@rolldown/plugin-babel';

export default defineConfig({
  plugins: [
    react(),
    babel({ presets: [reactCompilerPreset()] }), // React Compiler
  ],
});
// npm create vite@latest mon-app -- --template react-ts

Cas d'usage : SPA React, back-office interne, prototypes, bibliothèque de composants (avec Storybook ou Vitest).

Anti-pattern : Choisir Vite seul pour un site public qui a besoin de SEO et de rendu serveur : préférer un framework (Next.js, React Router framework mode).
#tooling#ecosystem

why-did-you-render

React 🟡 Mid

Librairie de debugging qui alerte dans la console quand un composant se re-rend inutilement (mêmes props mais nouvelle référence).

Comme un détecteur de fuite d'eau : il te montre exactement ou tu perds des performances.

// wdyr.js (importer en premier)
import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';
whyDidYouRender(React);

// Sur un composant specifique
MyComponent.whyDidYouRender = true;

Cas d'usage : Debugger les re-rendus excessifs pendant le développement pour trouver les fuites de performance.

#performance#devtools#debugging

Zod + React Hook Form

React 🟡 Mid

Intégration de Zod (validation de schémas) avec React Hook Form via @hookform/resolvers pour une validation typesafe partageable entre client et serveur.

Comme un contrat notarié pour un formulaire : les règles sont définies une fois et appliquées partout.

const schema = z.object({
  email: z.string().email('Email invalide'),
  age: z.number().min(18, 'Minimum 18 ans'),
});
type FormData = z.infer<typeof schema>;

const { register, handleSubmit } = useForm<FormData>({
  resolver: zodResolver(schema),
});

Cas d'usage : Formulaires avec validation complexe et types TypeScript inferes automatiquement depuis le schema.

#forms#typescript#validation

Zustand

React 🟡 Mid? entretien

Librairie de state management minimaliste sans boilerplate. Un store = une fonction, pas de Provider requis, compatible avec le rendu concurrent.

Comme un carnet de notes partage au bureau : tout le monde y accède directement, sans passer par un secrétaire (Provider).

const useStore = create((set) => ({
  count: 0,
  increment: () => set((s) => ({ count: s.count + 1 })),
}));

function Counter() {
  const count = useStore((s) => s.count);
  return <span>{count}</span>;
}

Cas d'usage : État global simple a moyen sans la complexité de Redux, idéal pour les projets de toute taille.

Anti-pattern : Ne pas utiliser de sélecteurs, souscrivant a tout le store et causant des re-rendus inutiles.
#state#interview#ecosystem