hydrateRoot
hydrateRoot vous permet d’afficher des composants React dans un nœud DOM du navigateur dont le HTML a été préalablement généré par react-dom/server.
const root = hydrateRoot(domNode, reactNode, options?)- Référence
- Utilisation
- Hydrater du HTML généré côté serveur
- Hydrater un document entier
- Réduire au silence les erreurs d’hydratation incontournables
- Différencier les contenus côté client et côté serveur
- Mettre à jour un composant racine hydraté
- Afficher un dialogue lors d’erreurs non capturées
- Afficher les erreurs de Périmètres d’Erreurs
- Afficher un dialogue lors d’erreurs récupérables
- Dépannage
Référence
hydrateRoot(domNode, reactNode, options?)
Appelez hydrateRoot pour « attacher » React à du HTML existant préalablement généré par React dans un environnement serveur.
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, reactNode);React s’attachera au HTML existant à l’intérieur de domNode, et prendra la main sur la gestion du DOM à l’intérieur. Une appli entièrement construite avec React n’aura généralement qu’un seul appel à hydrateRoot, pour le composant racine.
Voir d’autres exemples ci-dessous.
Paramètres
-
domNode: un élément DOM généré comme élément racine côté serveur. -
reactNode: un nœud React utilisé pour afficher le HTML existant. Ce sera généralement un bout de JSX du genre<App />, généré via une méthodereact-dom/servertelle querenderToPipeableStream(<App />). -
optionsoptionnelles : un objet avec des options pour la racine React.- Canary uniquement
onCaughtErroroptionelle : fonction de rappel appelée lorsque React capture une erreur au sein d’un Périmètre d’Erreur. Appelée avec l’errorcapturée par le Périmètre d’Erreur, et un objeterrorInfocontenant lacomponentStack. - Canary uniquement
onUncaughtErroroptionnelle : fonction de rappel appelée lorsqu’une erreur est levée sans être capturée par un Périmètre d’Erreur. Appelée avec l’errorlevée par React et un objeterrorInfocontenant lacomponentStack. onRecoverableErroroptionnel : fonction de rappel appelée lorsque React retombe automatiquement sur ses pieds suite à une erreur. Appelée avec l’errorlevée par React et un objeterrorInfocontenant lacomponentStack. Certaines de ces erreurs peuvent exposer leur cause originelle danserror.cause.identifierPrefixoptionnel : un préfixe textuel utilisé pour les ID générés paruseId. Pratique pour éviter les conflits entre les ID au sein de racines multiples sur une même page.
- Canary uniquement
Valeur renvoyée
hydrateRoot renvoie un objet avec deux méthodes : render et unmount.
Limitations
hydrateRoot()s’attend à ce que le contenu affiché soit identique au contenu généré côté serveur. Vous devriez considérer tout écart comme un bug et le corriger.- En mode développement, React vous avertira de tout écart de correspondance durant l’hydratation. Vous n’avez aucune garantie que les différences d’attributs seront résolues. C’est important pour des raisons de performances parce que dans la plupart des applis, les écarts sont rares, aussi valider tout le balisage serait d’une lourdeur prohibitive.
- Vous n’aurez probablement qu’un seul appel à
hydrateRootdans votre appli. Si vous utilisez un framework, il le fait peut-être pour vous. - Si votre appli est entièrement côté client, sans HTML déjà généré par le serveur, appeler
hydrateRoot()n’est pas autorisé. Utilisez plutôtcreateRoot().
root.render(reactNode)
Appelez root.render pour mettre à jour un composant React au sein d’une racine React hydratée associée à un élément DOM du navigateur.
root.render(<App />);React mettra à jour <App /> dans le root hydraté.
Voir d’autres exemples ci-dessous.
Paramètres
reactNode: un nœud React que vous souhaitez mettre à jour. Ce sera généralement un bout de JSX du genre<App />, mais vous pouvez aussi passer un élément React créé aveccreateElement(), une chaîne de caractères, un nombre,nullouundefined.
Valeur renvoyée
root.render renvoie undefined.
Limitations
- Si vous appelez
root.renderavant que la racine n’ait terminé son hydratation, React effacera tout le HTML produit par le serveur et basculera la racine entière vers un rendu côté client.
root.unmount()
Appelez root.unmount pour détruire l’arborescence de rendu au sein d’une racine React.
root.unmount();Une appli entièrement construite avec React n’appellera généralement pas root.unmount.
C’est principalement utile si le nœud DOM de votre racine React (ou un de ses ancêtres) est susceptible d’être retiré du DOM par du code tiers. Imaginez par exemple une gestion d’onglet basée sur jQuery qui retire les onglets inactifs du DOM. Si un onglet est retiré, tout ce qu’il contient (y compris d’éventuelles racines React) sera également retiré du DOM. Dans un tel cas, vous devez dire à React de « cesser » de gérer le contenu de la racine retirée en appelant root.unmount. Si vous ne le faisiez pas, les composants au sein de la racine retirée ne pourraient pas être nettoyés et libérer leurs ressources globales, telles que des abonnements.
Un appel à root.unmount démontera tous les composants dans cette racine et « détachera » React du nœud DOM racine, y compris pour la gestion événementielle et les états de l’arbre.
Paramètres
root.unmount ne prend aucun paramètre.
Returns
root.unmount renvoie undefined.
Limitations
-
Appeler
root.unmountdémontera tous les composants dans cette racine et « détachera » React du nœud DOM racine. -
Une fois que vous avez appelé
root.unmount, vous ne pouvez plus rappelerroot.rendersur cette même racine. Tenter d’appelerroot.rendersur une racine démontée lèvera une erreur “Cannot update an unmounted root” (« Impossible de mettre à jour une racine démontée », NdT).
Utilisation
Hydrater du HTML généré côté serveur
Si le HTML de votre appli est généré par react-dom/server, vous devez l’hydrater côté client.
import { hydrateRoot } from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);Ça hydratera le HTML issu du serveur au sein du nœud DOM du navigateur en utilisant le composant React de votre appli. En général, vous ne le ferez qu’une fois au démarrage. Si vous utilisez un framework, il le fait peut-être pour vous sous le capot.
Pour hydrater votre appli, React « attachera » la logique de vos composants au HTML initial généré par le serveur. L’hydratation transforme cet instantané initial du HTML, issu du serveur, en une appli pleinement interactive s’exécutant dans le navigateur.
import './styles.css'; import { hydrateRoot } from 'react-dom/client'; import App from './App.js'; hydrateRoot( document.getElementById('root'), <App /> );
Vous ne devriez pas avoir besoin de rappeler hydrateRoot ou de l’appeler ailleurs. À partir de ce moment, React prendra la main sur le DOM de votre application. Pour mettre à jour l’interface utilisateur (UI), vos composants mettront plutôt à jour l’état.
Hydrater un document entier
Les applis entièrement construites avec React peuvent produire le document entier via leur JSX, y compris la balise <html> :
function App() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles.css"></link>
<title>Mon appli</title>
</head>
<body>
<Router />
</body>
</html>
);
}Pour hydrater le document entier, passez la variable globale document comme premier argument dans votre appel à hydrateRoot :
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App />);Réduire au silence les erreurs d’hydratation incontournables
Si pour un élément spécifique, un attribut ou le contenu textuel s’avère inévitablement différent entre le serveur et le client (un horodatage, par exemple), vous pouvez choisir de réduire au silence l’avertissement d’écart d’hydratation.
Pour éviter les avertissements d’hydratation sur un élément spécifique, ajoutez-lui la prop suppressHydrationWarning={true} :
export default function App() { return ( <h1 suppressHydrationWarning={true}> Date actuelle : {new Date().toLocaleDateString()} </h1> ); }
Ça ne fonctionne qu’à un niveau de profondeur, et c’est vraiment une échappatoire. N’en abusez pas. React ne rattrapera le coup que pour les contenus textuels, il risque donc de rester quelques incohérences jusqu’au prochain rendu.
Différencier les contenus côté client et côté serveur
Si vous différenciez volontairement l’affichage entre le côté serveur et le côté client, vous pouvez faire un rendu en deux temps. Les composants qui affichent un contenu différent côté client peuvent lire une variable d’état telle que isClient, que vous pouvez mettre à true dans un Effet :
import { useState, useEffect } from "react"; export default function App() { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true); }, []); return ( <h1> {isClient ? 'Côté client' : 'Côté serveur'} </h1> ); }
De cette façon, la passe initiale de rendu afficher le même contenu que côté serveur, évitant toute incohérence ; mais une passe supplémentaire surviendra de façon synchrone juste après l’hydratation.
Mettre à jour un composant racine hydraté
Après que la racine a terminé l’hydratation, vous pouvez appeler root.render pour mettre à jour le composant React racine. Contrairement à createRoot, vous n’avez pas besoin de faire ça car le contenu initial était déjà présent dans le HTML.
Si vous appelez root.render après l’hydratation, et que la structure de l’arbre de composants correspond à celle déjà en place, React préservera l’état. Voyez comme vous pouvez taper quelque chose dans le champ, ce qui montre bien que les mises à jour issues d’appels répétés à render ne sont pas destructrices :
import { hydrateRoot } from 'react-dom/client'; import './styles.css'; import App from './App.js'; const root = hydrateRoot( document.getElementById('root'), <App counter={0} /> ); let i = 0; setInterval(() => { root.render(<App counter={i} />); i++; }, 1000);
Il est toutefois rare d’appeler root.render sur une racine hydratée. En général, vos composants mettront plutôt à jour l’état.
Afficher un dialogue lors d’erreurs non capturées
Par défaut, React affichera dans la console toute erreur non capturée. Pour implémenter votre propre signalement, vous pouvez fournir l’option onUncaughtError :
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onUncaughtError: (error, errorInfo) => {
console.error(
'Erreur non capturée',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);L’option onUncaughtError est une fonction avec deux arguments :
- L’error qui a été levée.
- Un objet errorInfo qui contient la componentStack de l’erreur.
Vous pouvez utiliser l’option onUncaughtError pour afficher des dialogues d’erreur :
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportUncaughtError} from "./reportError"; import "./styles.css"; import {renderToString} from 'react-dom/server'; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onUncaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportUncaughtError({ error, componentStack: errorInfo.componentStack }); } } });
Afficher les erreurs de Périmètres d’Erreurs
Par défaut, React affichera dans la console (au moyen de console.error) toute erreur capturée par un Périmètre d’Erreurs. Pour remplacer ce comportement, vous pouvez fournir l’option onCaughtError afin de traiter vous-mêmes les erreurs capturées par un Périmètre d’Erreurs :
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onCaughtError: (error, errorInfo) => {
console.error(
'Erreur capturée',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);L’option onCaughtError est une fonction avec deux arguments :
- L’error qui a été capturée par le Périmètre.
- Un objet errorInfo qui contient la componentStack de l’erreur.
Vous pouvez utiliser l’option onCaughtError pour afficher des dialogues d’erreur ou retirer les erreurs connues de la journalisation :
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportCaughtError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onCaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportCaughtError({ error, componentStack: errorInfo.componentStack }); } } });
Afficher un dialogue lors d’erreurs récupérables
React est susceptible de refaire le rendu d’un composant afin de tenter de retomber sur ses pieds lorsqu’un rendu lève une erreur. S’il réussit, React affichera en console une erreur récupérable, pour notifier le développeur. Pour remplacer ce comportement, vous pouvez fournir l’option onRecoverableError :
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onRecoverableError: (error, errorInfo) => {
console.error(
'Erreur récupérable',
error,
error.cause,
errorInfo.componentStack
);
}
}
);L’option onRecoverableError est une fonction avec deux arguments :
- L’error qui a été capturée par le Périmètre.
- Un objet errorInfo qui contient la componentStack de l’erreur.
Vous pouvez utiliser l’option onRecoverableError pour afficher des dialogues d’erreur ou retirer les erreurs connues de la journalisation :
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportRecoverableError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onRecoverableError: (error, errorInfo) => { reportRecoverableError({ error, cause: error.cause, componentStack: errorInfo.componentStack }); } });
Dépannage
J’ai une erreur : “You passed a second argument to root.render”
Une erreur courante consiste à passer les options de hydrateRoot à root.render(...) :
(« Avertissement : vous avez passé un second argument à root.render(…) alors qu’elle n’accepte qu’un argument. », NdT)
Pour corriger ça, passez ces options à hydrateRoot(...), pas à root.render(...) :
// 🚩 Incorrect : root.render ne prend qu’un argument.
root.render(App, {onUncaughtError});
// ✅ Correct : passez les options à hydrateRoot.
const root = hydrateRoot(container, <App />, {onUncaughtError});