Quelle est la nouvelle API obligatoire pour activer les fonctionnalités de React 18 ?
createRoot est la nouvelle API obligatoire.
Avant (React 17) :
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));Après (React 18) :
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);Pourquoi : Sans createRoot, vous restez en mode legacy et perdez TOUTES les nouvelles fonctionnalités (concurrent rendering, transitions, etc.).
Qu’est-ce que le Concurrent Rendering de React 18 ?
Le Concurrent Rendering permet à React d’interrompre un rendu en cours pour traiter quelque chose de plus urgent, puis de reprendre là où il s’était arrêté.
Analogie : Un serveur de restaurant qui peut interrompre une commande pour gérer une urgence, puis revenir à sa tâche.
Avantage : L’interface reste fluide même pendant des rendus coûteux.
Activation : Automatique avec createRoot, mais nécessite useTransition ou useDeferredValue pour marquer les mises à jour non-urgentes.
Quel est le mnémonique CATBUS pour retenir les nouveautés de React 18 ?
CATBUS (comme le chat-bus de Totoro) :
Phrase : “Le CATBUS de React 18 vous transporte vers des interfaces plus fluides !”
Qu’est-ce que l’Automatic Batching de React 18 ?
L’Automatic Batching regroupe automatiquement TOUTES les mises à jour de state en un seul re-rendu.
Avant React 18 :
async function handleSubmit() {
await saveData();
setCount(c => c + 1); // Re-rendu 1
setFlag(f => !f); // Re-rendu 2
}Avec React 18 :
async function handleSubmit() {
await saveData();
setCount(c => c + 1); // Groupé
setFlag(f => !f); // 1 seul re-rendu !
}Fonctionne partout : setTimeout, fetch, Promises, event listeners…
Opt-out : Utilisez flushSync() si vous avez besoin de forcer un re-rendu immédiat.
Que retourne useTransition et comment l’utiliser ?
useTransition retourne un tableau [isPending, startTransition].
isPending : booléen indiquant si une transition est en cours
startTransition : fonction pour envelopper les mises à jour non-urgentes
Exemple :
```
const [isPending, startTransition] = useTransition();
function handleClick() {
// URGENT : input réactif
setQuery(value);
// NON-URGENT : peut attendre
startTransition(() => {
setFilteredResults(filter(value));
});
}
return (
<>
{isPending && <Spinner></Spinner>}
<Results></Results>
</>
);
```
Utiliser quand : Vous contrôlez le setState qui cause un rendu lent.
Quelle est la différence entre useTransition et useDeferredValue ?
Tableau comparatif :
useTransition :
const [isPending, startTransition] = useTransition(); startTransition(() => setState(value));
useDeferredValue :
const deferredQuery = useDeferredValue(props.query);
Règle : useTransition quand vous CONTRÔLEZ, useDeferredValue quand vous RECEVEZ.
Aspect | useTransition | useDeferredValue |
|——–|—————|——————|
| Contrôle | Vous contrôlez le setState | Vous recevez une valeur |
| Retour | [isPending, startTransition] | valeur différée |
| Usage | Envelopper un setState | Différer une prop |
Que va afficher ce code en Strict Mode React 18 ?
```
function App() {
useEffect(() => {
console.log(‘monté’);
return () => console.log(‘démonté’);
}, []);
return <div>Hello</div>;
}
~~~
Résultat en Strict Mode :
monté démonté monté
Explication : Le Strict Mode de React 18 simule un démontage/remontage pour vérifier que vos effets sont correctement nettoyés.
Pourquoi : Prépare aux futures fonctionnalités comme l’Offscreen API où les composants peuvent être “mis en pause”.
Important : Ce comportement n’existe qu’en développement, pas en production.
Leçon : Toujours inclure une fonction de cleanup dans useEffect !
À quoi sert useId et quand NE PAS l’utiliser ?
useId génère des IDs uniques et stables pour l’accessibilité.
Utiliser pour :
function FormField({ label }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} />
</>
);
}NE JAMAIS utiliser pour les clés de liste !
❌ Mauvais :
{items.map((item, i) => (
<li key={`${useId()}-${i}`}>{item}</li> // INTERDIT
))}✅ Correct :
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}Pourquoi useId : Stable entre serveur et client (SSR compatible).
Quel est le problème avec ce code React 18 ?
```
function Search({ query }) {
const [isPending, startTransition] = useTransition();
startTransition(() => {
// query est une prop, pas un state local
});
}
~~~
Problème : On ne peut pas “différer” une prop avec useTransition !
Explication : useTransition est fait pour envelopper vos propres setState. Si vous recevez une valeur en props, utilisez useDeferredValue.
Solution :
```
function Search({ query }) {
// Différer la VALEUR reçue
const deferredQuery = useDeferredValue(query);
return <Results query={deferredQuery} />;
}
```
Règle :
- useTransition : quand VOUS contrôlez le setState
- useDeferredValue : quand vous RECEVEZ une valeur
Comment fonctionne l’ordre d’exécution des effets en React 18 ?
Ordre d’exécution :
Exemple :
```
useInsertionEffect(() => {
// 1. Injecter les styles
});
useLayoutEffect(() => {
// 3. Mesurer le DOM
});
useEffect(() => {
// 5. Effets de bord (fetch, subscriptions)
});
```
useInsertionEffect : Réservé aux bibliothèques CSS-in-JS (styled-components, Emotion).
Comment migrer de React 17 à React 18 en 3 étapes ?
Migration minimale en 3 étapes :
1. Mettre à jour les packages :
npm install react@18 react-dom@18
2. Modifier index.js :
```
// Avant
import ReactDOM from ‘react-dom’;
ReactDOM.render(<App></App>, document.getElementById(‘root’));
// Après
import { createRoot } from ‘react-dom/client’;
const root = createRoot(document.getElementById(‘root’));
root.render(<App></App>);
```
3. Vérifier les effets : S’assurer que tous les useEffect ont une fonction de cleanup.
Bonus automatique : Vous bénéficiez immédiatement de l’Automatic Batching !
À quoi sert useSyncExternalStore ?
useSyncExternalStore permet de s’abonner à des stores externes de manière compatible avec le concurrent rendering.
Destiné aux auteurs de bibliothèques (Redux, Zustand, etc.)
Exemple :
function useWindowWidth() {
return useSyncExternalStore(
// subscribe
(callback) => {
window.addEventListener('resize', callback);
return () => window.removeEventListener('resize', callback);
},
// getSnapshot (client)
() => window.innerWidth,
// getServerSnapshot (SSR)
() => 1024
);
}3 paramètres :
1. Fonction d’abonnement
2. Getter de la valeur (client)
3. Getter de la valeur (serveur, optionnel)
Vous n’utiliserez probablement jamais ce hook directement.
Quelles sont les améliorations de Suspense dans React 18 ?
Suspense amélioré en React 18 :
1. SSR avec Streaming :
Le HTML est envoyé progressivement au navigateur.
2. Hydratation Sélective :
React hydrate les composants par ordre de priorité.
3. Interruption :
Si l’utilisateur interagit avec un composant non-hydraté, React le priorise.
Exemple :
```
<Suspense fallback={<Spinner></Spinner>}>
<SlowComponent></SlowComponent>
</Suspense>
<Suspense fallback={<CommentsSkeleton></CommentsSkeleton>}>
<Comments></Comments>
</Suspense>
```
Avantage : L’utilisateur peut voir et interagir avec les parties rapides pendant que les parties lentes chargent.
Comment désactiver le batching automatique si nécessaire ?
Utilisez flushSync pour forcer des re-rendus séparés.
```
import { flushSync } from ‘react-dom’;
function handleClick() {
flushSync(() => {
setCount(c => c + 1);
});
// DOM mis à jour ici
flushSync(() => {
setFlag(f => !f);
});
// DOM mis à jour ici aussi
}
```
Cas d’usage rare :
- Mesurer le DOM entre deux mises à jour
- Intégration avec du code legacy
- Animations spécifiques
Attention : flushSync nuit aux performances. Évitez-le sauf si absolument nécessaire.
99% du temps : Laissez le batching automatique faire son travail !
Comment créer un champ de recherche optimisé avec les Transitions ?
Pattern complet de recherche avec Transitions :
```
import { useState, useTransition } from ‘react’;
function SearchList({ items }) {
const [query, setQuery] = useState(‘’);
const [filtered, setFiltered] = useState(items);
const [isPending, startTransition] = useTransition();
function handleSearch(e) {
const value = e.target.value;
// URGENT : input réactif
setQuery(value);
// NON-URGENT : filtrage
startTransition(() => {
setFiltered(items.filter(item =>
item.includes(value)
));
}); }return (
<>
<input value={query} onChange={handleSearch} />
{isPending && <span>Recherche…</span>}
<ul style={{ opacity: isPending ? 0.7 : 1 }}>
{filtered.map(item => <li key={item}>{item}</li>)}
</ul>
</>
);
}
```
Points clés :
- 2 states : un urgent (query), un différé (filtered)
- isPending pour feedback visuel
- Opacité réduite pendant la transition