Directive qui marque un composant comme Client Component, activant l'interactivite (state, effets, handlers). Frontiere 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.
Directive qui marque une fonction comme Server Action : elle s'execute sur le serveur mais peut etre appelee depuis le client, comme un RPC transparent.
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 donnees (formulaires, CRUD) sans creer de route API separee.
Fonction associee a une route qui gere les mutations de donnees (POST, PUT, DELETE) via les soumissions de formulaires natifs.
Comme la boite aux lettres d'un bureau : tu deposes ton courrier (formulaire), le bureau (action) le traite.
export async function createAction({ request }) {
const formData = await request.formData();
await api.createUser(Object.fromEntries(formData));
return redirect('/users');
}
// Route: { path: 'users/new', action: createAction }Cas d'usage : Gerer les soumissions de formulaires avec revalidation automatique des loaders apres mutation.
React 18 regroupe automatiquement plusieurs mises a jour d'etat en un seul re-rendu, meme dans les callbacks asynchrones et les timeouts.
Comme un facteur qui attend d'avoir tout le courrier du quartier avant de faire sa tournee, 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 : Amelioration automatique des performances sans intervention du developpeur en React 18+.
Analyse visuelle du contenu du bundle JavaScript pour identifier les dependances lourdes et les opportunites de code splitting.
Comme une radio de valise a l'aeroport : 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 buildCas d'usage : Diagnostiquer pourquoi le bundle est trop gros et prioriser les optimisations de taille.
Technique qui decoupe le bundle JS en morceaux charges a la demande via React.lazy et import() dynamique, reduisant le temps de chargement initial.
Comme un buffet a volonte : 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.
Les differentes phases d'un composant : montage, mise a jour et demontage. En hooks, useEffect gere ces phases.
Comme la vie d'un employe : embauche (mount), promotions (update), depart (unmount).
useEffect(() => {
console.log('Monte ou mis a jour');
return () => console.log('Demonte / cleanup');
}, [dep]); // se relance si dep changeCas d'usage : Gerer les effets de bord comme les abonnements, les appels API ou les timers.
Pattern ou un composant parent partage son etat implicitement avec ses enfants via Context, offrant une API declarative et flexible.
Comme un menu deroulant 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, accordeons, menus) necessitant une API flexible et composable.
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 preparation d'une commande pour repondre 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 reactive pendant des rendus lourds (longues listes, calculs complexes).
Pattern qui separe les composants en deux types : Container (logique, data fetching) et Presentational (UI pure, recoit des props). Moins pertinent avec les hooks.
Comme un realisateur (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 reutilisables et testables en les separant de la logique.
Systeme de partage de donnees sans prop drilling via createContext, Provider et useContext. Attention : chaque changement de valeur re-rend TOUS les consommateurs.
Comme un haut-parleur dans un magasin : tout le monde entend l'annonce, meme ceux qui ne sont pas concernes.
const AuthCtx = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
return (
<AuthCtx.Provider value={{ user, setUser }}>
{children}
</AuthCtx.Provider>
);
}Cas d'usage : Donnees peu changeantes (theme, auth, locale) partagees a travers l'arbre.
Controlled : la valeur est geree par React (via state + onChange). Uncontrolled : la valeur est geree 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 lireCas d'usage : Controlled pour validation en temps reel, uncontrolled pour formulaires simples ou performance critique.
Fonction RTK qui genere automatiquement un thunk avec les actions pending/fulfilled/rejected pour les operations asynchrones.
Comme un livreur avec suivi : tu sais quand il part (pending), arrive (fulfilled) ou echoue (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 etats de chargement et d'erreur dans Redux.
Fonction de Reselect (integree a RTK) qui cree des selecteurs memoises. Le resultat n'est recalcule que si les inputs changent, evitant des calculs redondants.
Comme un formulaire pre-rempli : tant que les donnees source ne changent pas, le calcul derive est reutilise.
const selectUsers = (state) => state.users.list;
const selectActiveUsers = createSelector(
[selectUsers],
(users) => users.filter(u => u.active)
);
// Ne refiltre que si state.users.list changeCas d'usage : Derivation de donnees couteuses depuis le store Redux (filtrage, tri, aggregation).
Le serveur envoie un HTML vide avec un bundle JS. Le navigateur execute React pour generer le DOM. Mauvais pour le SEO et le premier affichage.
Comme recevoir des meubles en kit : le client (navigateur) doit tout assembler lui-meme 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.
Fonction JavaScript prefixee par 'use' qui encapsule de la logique reutilisable composee d'autres hooks. Chaque appel cree sa propre instance de state.
Comme une recette de cuisine : tu la reutilises dans differents repas, mais chaque plat a ses propres ingredients (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 reutiliser de la logique stateful entre composants (fetch, debounce, form, auth).
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 piece de voiture sur un banc d'essai reel 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) necessitant un vrai navigateur.
Utilitaire RTK qui genere un ensemble de reducers et selecteurs pour gerer des collections normalisees (ids + entities) avec operations 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'entites (utilisateurs, produits, commentaires) necessitant des operations CRUD normalisees.
Composant classe qui intercepte les erreurs JavaScript dans son sous-arbre et affiche un UI de secours au lieu de crasher toute l'application.
Comme un fusible electrique : il saute pour proteger le reste du circuit.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
render() {
if (this.state.hasError) return <p>Oups!</p>;
return this.props.children;
}
}Cas d'usage : Encapsuler des sections critiques pour que le reste de l'app continue de fonctionner en cas d'erreur.
Conteneur invisible qui permet de retourner plusieurs elements sans ajouter de noeud DOM supplementaire.
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 : Eviter les divs inutiles qui cassent le layout CSS ou la semantique HTML.
Composants qui fournissent la logique et l'accessibilite sans imposer de style ni de markup. L'utilisateur controle entierement 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 etre stylisables sans contrainte.
Fonction qui prend un composant et retourne un nouveau composant enrichi. Pattern classique de reutilisation de logique, largement remplace par les hooks.
Comme un cadre photo qui embellit toute photo qu'on y met : le HOC ajoute des fonctionnalites 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.
Pattern TanStack Query pour le scroll infini ou le 'charger plus'. useInfiniteQuery gere automatiquement les pages, le curseur et la concatenation des resultats.
Comme un rouleau de papier toilette : tu en deroules plus quand tu en as besoin, page apres page.
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
// data.pages contient toutes les pages chargeesCas d'usage : Feeds sociaux, listes de produits, tout contenu pagine avec scroll infini ou bouton 'charger plus'.
Permet d'intercepter une route pour afficher son contenu dans le layout actuel (ex: modale) au lieu de naviguer vers la page complete.
Comme un apercu rapide au survol : tu vois le contenu sans quitter ta page, mais l'URL directe montre la version complete.
// 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 completeCas d'usage : Photos en modale (Instagram-like), apercu de produit, detail de notification sans quitter le feed.
Methode TanStack Query qui marque des requetes en cache comme perimees, declenchant un refetch automatique si elles sont actuellement utilisees.
Comme tirer la chasse d'eau du cache : les donnees perimees sont evacuees 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 : Apres une mutation reussie, forcer le rechargement des donnees affectees dans le cache.
Combine SSG et SSR : les pages sont generees statiquement mais regenerees en arriere-plan apres un delai configurable (revalidate). Introduit par Next.js.
Comme un menu du jour affiche a l'entree : il est pre-imprime mais le serveur le remplace discretement chaque jour.
// Next.js App Router
export const revalidate = 3600; // regenere toutes les heures
export default async function Products() {
const products = await getProducts();
return <ProductGrid products={products} />;
}Cas d'usage : Pages e-commerce, catalogues : contenu relativement stable mais qui doit se mettre a jour sans rebuild complet.
Gestion d'etat atomique pour React. Chaque atome est une unite de state independante, composable et reactive, sans Provider obligatoire.
Comme des briques LEGO : chaque brique (atome) est independante 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'etat global.
Syntaxe qui permet d'ecrire du HTML dans du JavaScript. Le compilateur le transforme en appels React.createElement().
C'est comme un moule a gateau : 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 decrire l'interface de maniere declarative.
Attribut special qui aide React a identifier chaque element dans une liste. Permet une reconciliation efficace sans reconstruire tout le DOM.
Comme les numeros de dossards dans une course : React sait qui est qui meme 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 eviter des bugs de rendu et ameliorer les performances.
Fonction associee a une route qui charge les donnees avant le rendu du composant. Les donnees sont accessibles via useLoaderData().
Comme un serveur qui prepare ta table avant que tu arrives : tout est pret quand tu t'assois.
// Definition
export async function userLoader({ params }) {
return fetch(`/api/users/${params.id}`);
}
// Route: { path: 'users/:id', loader: userLoader, element: <User /> }
// Composant
function User() {
const user = useLoaderData();
return <p>{user.name}</p>;
}Cas d'usage : Pre-charger les donnees d'une page avant le rendu pour eviter les waterfalls de fetching.
Outil qui intercepte les requetes reseau 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 cinema : le vrai acteur (API) est absent mais la scene (test) se joue quand meme.
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 developpement frontend sans backend disponible.
Operation d'ecriture (POST, PUT, DELETE) geree par useMutation avec callbacks onSuccess/onError et possibilite de mises a jour optimistes.
Comme un formulaire de La Poste : tu le remplis (mutate), tu sais si c'est envoye (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 operation d'ecriture vers le serveur avec invalidation automatique du cache apres succes.
Routes imbriquees ou les routes enfants s'affichent a l'interieur du layout de leur route parent via Outlet, permettant des layouts partages et une navigation hierarchique.
Comme des poupees russes : chaque layout contient le suivant, et seul le contenu le plus interne change.
// /dashboard -> Layout + DashboardHome
// /dashboard/settings -> Layout + Settings
<Route path="dashboard" element={<DashLayout />}>
<Route index element={<DashHome />} />
<Route path="settings" element={<Settings />} />
</Route>Cas d'usage : Applications avec des layouts hierarchiques (sidebar fixe, breadcrumbs dynamiques, multi-step wizards).
Systeme de routage Next.js base sur le filesystem avec Server Components par defaut, layouts imbriques, et conventions de fichiers (page, layout, loading, error).
Comme un immeuble ou chaque dossier est un etage et chaque fichier est une piece avec un role precis.
// 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 autoCas d'usage : Applications React full-stack avec SSR, RSC, routing et API integres.
Code qui s'execute avant chaque requete dans Next.js, au niveau Edge. Permet la redirection, la reecriture d'URL, l'ajout de headers et l'auth.
Comme un vigile a l'entree d'un batiment : il verifie ton badge avant de te laisser entrer dans n'importe quelle piece.
// middleware.ts (racine du projet)
import { NextResponse } from 'next/server';
export function middleware(request) {
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*'] };Cas d'usage : Auth, redirection A/B testing, geolocalisation, rate limiting au niveau Edge.
Pattern combinant useOptimistic et useFormStatus pour afficher immediatement le resultat d'une soumission de formulaire avant la confirmation serveur.
Comme un tchat ou ton message s'affiche instantanement 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 percue nuit a l'UX : chat, commentaires, listes de taches.
Technique qui met a jour l'UI immediatement avant la confirmation serveur, puis annule en cas d'echec. Ameliore la perception de vitesse.
Comme celebrer un but avant la validation VAR : si le but est annule, on revient a l'etat precedent.
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 frequentes (like, toggle, reorder) ou la latence reseau degrade l'UX.
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.
function Layout() {
return (
<div>
<Navbar />
<main><Outlet /></main> {/* route enfant ici */}
<Footer />
</div>
);
}Cas d'usage : Layouts partages (sidebar, header, footer) avec du contenu qui change selon la route active.
Fonctionnalite Next.js App Router qui permet de rendre plusieurs pages simultanement dans le meme layout via des slots nommes (@slot).
Comme un ecran divise en deux sur ta TV : tu regardes deux chaines en meme temps dans le meme 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 independants, modales comme routes, vues conditionnelles.
Fonctionnalite Next.js experimentale combinant une coquille statique (servie depuis le CDN) avec des trous dynamiques remplis en streaming. Le meilleur des deux mondes.
Comme un journal avec des encarts pub personnalises : le journal est imprime d'avance, mais les pubs changent par lecteur.
// La coquille statique est pre-rendue
// Les parties dynamiques sont wrappees dans Suspense
export default function Page() {
return (
<StaticHeader />
<Suspense fallback={<CartSkeleton />}>
<DynamicCart /> {/* stream dynamique */}
</Suspense>
);
}Cas d'usage : Pages e-commerce avec header statique rapide et panier/prix dynamiques streames.
Option TanStack Query qui fournit des donnees temporaires pendant le chargement initial, sans les stocker dans le cache. Evite 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 donnees precedentes pendant une transition entre pages ou profils utilisateur.
Composants qui peuvent changer leur element HTML racine via une prop 'as' ou 'component', tout en conservant un typage TypeScript correct.
Comme un costume transformable : le meme vetement 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 meme composant stylise doit s'adapter a differents elements HTML semantiques.
Permet de rendre un composant enfant dans un noeud DOM situe en dehors de la hierarchie 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 cachee 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 deroulants qui doivent echapper au overflow:hidden du parent.
Methode de TanStack Query pour pre-charger des donnees en cache avant qu'elles ne soient necessaires, eliminant le temps de chargement visible.
Comme prechauffer le four avant d'enfourner : quand tu en as besoin, tout est deja pret.
// Prefetch au hover d'un lien
<Link
onMouseEnter={() => {
queryClient.prefetchQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
}}
/>Cas d'usage : Pre-charger les donnees de la prochaine page au survol d'un lien ou pendant un formulaire multi-etapes.
Composant React et onglet DevTools qui mesurent le cout de rendu d'un sous-arbre : frequence, duree et raison des re-rendus.
Comme un chronometre pour chaque coureur dans un relais : tu identifies qui ralentit l'equipe.
<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.
Principe ou l'application fonctionne sans JavaScript (formulaires HTML natifs, liens) puis s'ameliore 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 meme si le JS echoue.
Pattern qui fournit des fonctions (getInputProps, getToggleProps) retournant les props necessaires pour un element, fusionnant les props utilisateur avec celles du composant.
Comme un styliste qui te donne un ensemble complet : tu peux ajouter tes accessoires, il gere 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 gerer l'accessibilite tout en restant flexibles.
Donnees passees d'un composant parent a un composant enfant, en lecture seule. C'est le mecanisme principal de communication descendante.
Comme les ingredients 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 reutilisable en parametrant son comportement et son contenu.
Pattern qui utilise Context.Provider pour fournir des donnees ou des services a un sous-arbre de composants, sans prop drilling.
Comme le Wi-Fi d'un batiment : tous les appareils dans la zone y accedent sans cable 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.
Identifiant unique d'une requete dans TanStack Query, utilise pour le cache, la deduplication et l'invalidation. Generalement un tableau hierarchique.
Comme l'etiquette 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 cles de facon hierarchique pour pouvoir invalider a differents niveaux de granularite.
Architecture interne de React (depuis v16) qui decoupe le rendu en unites de travail interruptibles. Permet le rendu concurrent et la priorisation des mises a jour.
Comme un chef cuisinier qui peut interrompre la preparation d'un plat pour servir un client urgent, puis reprendre ou il en etait.
// 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 reactif meme avec des rendus lourds.
Librairie de formulaires performante basee sur les refs (uncontrolled). Minimise les re-rendus et offre une validation integree 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.
Librairie de routage standard pour React avec routes imbriquees, loaders de donnees, actions de mutation et navigation declarative.
Comme le plan d'un centre commercial avec les fleches directionnelles : chaque URL mene a la bonne boutique (composant).
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: 'users/:id', element: <User />, loader: userLoader },
]
}
]);Cas d'usage : SPA React sans framework (Vite + React) qui a besoin de navigation multi-pages.
Composants qui s'executent uniquement sur le serveur, avec acces direct aux bases de donnees et fichiers. Leur code n'est pas envoye au client, reduisant le bundle JS.
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 donnees sans interactivite : listes, articles, dashboards de lecture.
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 details d'implementation.
Comme un testeur mystere dans un restaurant : il teste l'experience 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'integration de composants React, focus sur le comportement utilisateur.
HOC qui memoize un composant : il ne se re-rend que si ses props changent (comparaison shallow par defaut).
Comme un videur de boite de nuit qui ne laisse passer que les nouvelles tetes : memes 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 : Composants enfants couteux qui re-rendent inutilement a cause du re-rendu du parent.
Algorithme de diffing qui compare deux arbres Virtual DOM pour determiner les changements minimaux a appliquer au vrai DOM. Complexite O(n) grace a des heuristiques.
Comme un jeu des 7 differences : React compare les deux images et ne corrige que les pixels differents.
// 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 reconciliation aide a ecrire du code performant et a bien utiliser les keys.
Boite a outils officielle de Redux qui simplifie la configuration du store, la creation 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 : Etat global complexe necessitant de la predictibilite, du time-travel debugging et du middleware.
Framework React full-stack qui embrasse les standards web (fetch, FormData, Response). Routes = fichiers, chaque route a loader/action/component co-localises.
Comme un restaurant local bio : il utilise les ingredients natifs (standards web) au lieu d'importer des produits exotiques.
// app/routes/users.tsx
export async function loader() {
return json(await getUsers());
}
export async function action({ request }) {
const form = await request.formData();
return createUser(Object.fromEntries(form));
}
export default function Users() {
const users = useLoaderData<typeof loader>();
}Cas d'usage : Applications full-stack privilegiant les standards web, l'accessibilite et le progressive enhancement.
Pattern ou un composant recoit une fonction en prop qui retourne du JSX, permettant de partager de la logique tout en laissant le controle du rendu a l'appelant.
Comme un cours de peinture 'apporte ta toile' : le professeur (composant) guide, mais tu decides du resultat.
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 reutilisable entre composants (souvent remplace par les custom hooks aujourd'hui).
Dossiers parentheses (group) dans App Router qui organisent les routes sans affecter l'URL. Permettent des layouts differents 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 dashboardCas d'usage : Separer les layouts (public vs dashboard) ou organiser le code sans impacter les URLs.
Solution de data fetching et caching integree a Redux Toolkit. Genere automatiquement les hooks, gere le cache, l'invalidation et les requetes optimistes.
Comme un assistant personnel qui passe les commandes, gere le stock et previent quand il faut reapprovisionner.
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'ecosysteme Redux.
Fonctions asynchrones cote serveur invoquees directement depuis des composants client, typiquement via l'attribut action d'un formulaire. Remplacent les routes API manuelles.
Comme commander par telephone : tu passes l'ordre (client) et la cuisine (serveur) execute, sans aller au comptoir (API route).
// action.ts
'use server';
export async function addTodo(formData: FormData) {
await db.todos.create({ text: formData.get('text') });
revalidatePath('/todos');
}
// component.tsx
<form action={addTodo}><input name="text" /><button>Add</button></form>Cas d'usage : Soumission de formulaires, CRUD, toute mutation serveur avec revalidation automatique.
Validation des donnees cote serveur en complement du client, car la validation client est contournable. Avec Server Actions, le meme schema Zod peut valider des deux cotes.
Comme un double controle de securite : 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 donnees : la validation client est UX, la validation serveur est securite.
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 electronique : 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 reconciliation de tout le sous-arbre.
Pattern qui permet au parent de passer du contenu JSX dans des emplacements nommes du composant enfant, via des props ou children specifiques.
Comme un formulaire administratif avec des cases a remplir : chaque case (slot) attend un contenu specifique.
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).
Les pages HTML sont generees au moment du build, pas a la requete. Resultat ultra-rapide puisque le CDN sert des fichiers statiques pre-generes.
Comme imprimer des flyers a l'avance : pas besoin de les creer a la demande, ils sont deja prets 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 requete.
Le serveur genere le HTML complet a chaque requete. Le navigateur affiche la page immediatement puis l'hydrate avec React pour la rendre interactive.
Comme un plat prepare au restaurant (serveur) vs un kit a assembler chez soi (CSR) : le client recoit un plat deja pret.
// 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 necessitant un bon SEO et un premier affichage rapide (e-commerce, blogs, landing pages).
Donnees internes et mutables d'un composant. Quand le state change, React re-rend le composant pour refleter la nouvelle valeur.
Comme le score dans un jeu video : 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 donnees dynamiques propres a un composant (formulaires, toggles, compteurs).
Pattern qui permet au consommateur d'un composant de personnaliser les transitions d'etat en fournissant son propre reducer, fusionnant avec le comportement par defaut.
Comme un pilote automatique avec override : l'avion vole seul mais le pilote peut reprendre le controle 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 generiques ou l'utilisateur doit pouvoir overrider le comportement interne.
Outil de developpement et documentation de composants UI en isolation. Chaque 'story' montre un etat 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 : Developper, tester visuellement et documenter les composants UI d'un design system.
Le serveur envoie le HTML en flux continu (stream) au lieu d'attendre que toute la page soit generee. Les parties lentes arrivent au fur et a mesure.
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 differentes : le header s'affiche vite, les donnees arrivent en stream.
Composant de developpement qui active des verifications supplementaires et double les rendus pour detecter les effets de bord impurs.
Comme un professeur qui fait refaire l'exercice deux fois pour verifier que le resultat est toujours le meme.
<React.StrictMode>
<App />
</React.StrictMode>
// En dev, useEffect s'execute 2 fois pour detecter les bugsCas d'usage : Activer en developpement pour detecter les effets de bord, les API depreciees et les problemes de cleanup.
Composant qui affiche un fallback pendant que ses enfants chargent des donnees ou du code asynchrone. Pilier du rendu concurrent.
Comme un ecran 'Chargement...' au cinema 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 donnees avec React Query.
Librairie de fetching de Vercel basee sur la strategie stale-while-revalidate : affiche les donnees en cache immediatement, puis revalide en arriere-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 legere a TanStack Query pour des cas de fetching simples.
Librairie de gestion de l'etat serveur (fetching, caching, synchronisation). Gere automatiquement le cache, la revalidation, le retry et les mises a jour optimistes.
Comme un concierge d'hotel : il anticipe tes besoins, garde tes affaires en cache et les rafraichit discretement.
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.
Nouvelle API React 19 qui permet de lire une Promise ou un Context directement dans le rendu, meme de maniere conditionnelle contrairement aux autres hooks.
Comme un lecteur universel : qu'on lui donne un CD (Promise) ou une cle USB (Context), il lit les deux.
function UserProfile({ userPromise }) {
const user = use(userPromise);
return <p>{user.name}</p>;
}
// Peut etre utilise conditionnellement
if (showDetails) { const data = use(promise); }Cas d'usage : Lire des donnees asynchrones dans les Server Components ou consommer un Context conditionnellement.
Hook qui memoize une fonction pour conserver la meme reference entre les rendus. Equivalent a useMemo(() => fn, deps).
Comme donner toujours le meme numero de telephone 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 : Passer un callback a un composant enfant memoize avec React.memo pour eviter ses re-rendus inutiles.
Hook qui lit la valeur du Context le plus proche dans l'arbre. Evite le prop drilling en partageant des donnees entre composants distants.
Comme la radio FM : tout le monde dans la zone capte le meme signal sans cable direct.
const ThemeCtx = createContext('light');
function App() {
return (
<ThemeCtx.Provider value="dark">
<Toolbar />
</ThemeCtx.Provider>
);
}
function Button() { const theme = useContext(ThemeCtx); }Cas d'usage : Partager des donnees globales (theme, langue, user) sans passer par chaque niveau de props.
Hook qui retourne une version differee d'une valeur, permettant a React de prioriser les mises a jour urgentes et de differer le re-rendu du contenu non critique.
Comme un echo : ta voix (valeur originale) arrive d'abord, puis l'echo (valeur differee) suit avec un leger decalage.
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 controles pas le setter de l'etat (props venant du parent).
Hook pour synchroniser un composant avec un systeme externe (API, DOM, timer). S'execute apres le rendu, avec un tableau de dependances pour controler quand il se relance.
Comme un abonnement a un journal : tu t'abonnes (setup), tu recois les numeros (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 changeCas d'usage : Appels API, abonnements WebSocket, synchronisation avec le DOM ou des librairies externes.
Hook qui donne l'etat de soumission du formulaire parent (pending, data, method, action). Doit etre utilise dans un composant enfant du <form>.
Comme un indicateur lumineux sur une machine a cafe : il te dit si elle est en train de preparer (pending) ou prete.
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Envoi...' : 'Envoyer'}
</button>
);
}Cas d'usage : Desactiver le bouton submit et afficher un spinner pendant la soumission d'un formulaire avec Server Actions.
Hook qui genere un identifiant unique stable entre serveur et client, utile pour les attributs d'accessibilite comme htmlFor et aria-describedby.
Comme un tampon officiel : le meme numero 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 : Generer des IDs pour les associations label/input sans risque de collision en SSR.
Hook qui personnalise la valeur exposee par un ref forwarde. Permet de n'exposer que certaines methodes d'un composant enfant au parent.
Comme un interphone d'immeuble : tu choisis quels boutons exposer, pas toutes les cles de l'appartement.
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; }
}));
return <input ref={inputRef} />;
});Cas d'usage : Exposer une API imperative limitee a un composant enfant (focus, scroll, reset).
Hook qui s'execute avant toute mutation DOM, concu 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 prets 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 reserve aux librairies CSS-in-JS (styled-components, Emotion) pour injecter du CSS.
Identique a useEffect mais s'execute de maniere synchrone apres 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 eviter un flash visuel.
Hook qui memoize le resultat d'un calcul couteux et ne le recalcule que si ses dependances changent.
Comme mettre un plat au frigo : tu ne recuisines pas si les ingredients n'ont pas change, tu ressers le meme plat.
const sortedItems = useMemo(
() => items.sort((a, b) => a.price - b.price),
[items]
);
// Ne retrie que si 'items' changeCas d'usage : Calculs couteux (tri, filtrage, transformations) qui ralentissent le rendu si recalcules a chaque fois.
Hook experimental qui permet d'afficher un etat optimiste pendant une action asynchrone, automatiquement annule en cas d'erreur.
Comme cocher 'lu' sur un message avant que le serveur confirme : si ca echoue, la coche disparait.
const [optimisticLikes, addOptimistic] = useOptimistic(
likes,
(state, newLike) => [...state, newLike]
);
async function handleLike() {
addOptimistic({ id: 'temp', user: 'moi' });
await saveLike();
}Cas d'usage : Likes, commentaires, toggles : feedback instantane a l'utilisateur pendant la requete serveur.
Hook alternatif a useState pour gerer un etat complexe avec une logique de type action/reducer, inspire de Redux.
Comme un standard telephonique : tu envoies un message (action) et l'operateur (reducer) decide 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 : Etats complexes avec plusieurs sous-valeurs interdependantes ou quand la logique de transition est elaboree.
Hook qui retourne un objet mutable {current} persistant entre les rendus, sans declencher de re-rendu quand il change. Sert a referencer des elements 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 : Acceder au DOM (focus, scroll), stocker des IDs de timer, garder la valeur precedente d'un state.
Hook qui ajoute un etat 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 etat local simple : champs de formulaire, toggles, compteurs, donnees UI.
Hook pour s'abonner a un store externe (non-React) de maniere compatible avec le rendu concurrent, evitant les tearing (desynchro visuelle).
Comme un ecran d'aeroport synchronise : tous les passagers voient les memes horaires, pas de version perimee.
const width = useSyncExternalStore(
(cb) => { window.addEventListener('resize', cb); return () => window.removeEventListener('resize', cb); },
() => window.innerWidth,
() => 1024 // server snapshot
);Cas d'usage : Integration avec des stores externes (Zustand, Redux interne) ou des API navigateur (resize, online).
Hook qui marque une mise a jour comme non urgente, permettant a React de la differer pour garder l'interface reactive pendant les operations lourdes.
Comme un panneau 'en cours de renovation' : les clients voient l'ancien decor pendant que tu renoves en arriere-plan.
const [isPending, startTransition] = useTransition();
function handleSearch(query) {
startTransition(() => {
setFilteredResults(heavyFilter(query));
});
}
// isPending = true pendant le calculCas d'usage : Filtrage de longues listes, navigation entre onglets, tout ce qui peut bloquer l'interface.
State management base sur des proxies JavaScript. L'etat est mutable directement et Valtio detecte 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 : Developpeurs preferant la mutation directe d'objets, prototypage rapide, migration depuis MobX.
Representation legere du DOM reel en memoire. 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 DOMCas d'usage : Mecanisme interne qui optimise les mises a jour du DOM sans intervention manuelle.
Technique de rendu qui n'affiche que les elements visibles dans le viewport, meme pour des listes de milliers d'items. Les elements hors ecran ne sont pas montes.
Comme un ascenseur panoramique : tu ne vois que les etages devant toi, pas les 100 etages en meme 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 10000Cas d'usage : Listes ou tableaux de plus de quelques centaines d'elements (logs, contacts, produits).
Librairie de debugging qui alerte dans la console quand un composant se re-rend inutilement (memes props mais nouvelle reference).
Comme un detecteur 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 developpement pour trouver les fuites de performance.
Integration de Zod (validation de schemas) avec React Hook Form via @hookform/resolvers pour une validation typesafe partageable entre client et serveur.
Comme un contrat notarie pour un formulaire : les regles sont definies une fois et appliquees 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.
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 accede directement, sans passer par un secretaire (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 : Etat global simple a moyen sans la complexite de Redux, ideal pour les projets de toute taille.
Autres stacks