hydrateRoot
hydrateRoot позволяет отображать компоненты React внутри узла DOM браузера, HTML-содержимое которого ранее было создано с помощью react-dom/server.
const root = hydrateRoot(domNode, reactNode, options?)
Справочник
hydrateRoot(domNode, reactNode, options?)
Вызовите hydrateRoot, чтобы «подключить» React к существующему HTML, который уже был отрисован React в серверной среде.
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, reactNode); React подключится к HTML внутри domNode и возьмёт на себя управление DOM внутри него. Приложение, полностью созданное с помощью React, обычно содержит только один вызов hydrateRoot с корневым компонентом.
Ниже приведено больше примеров.
Параметры
-
domNode: Элемент DOM, отрисованный на сервере в качестве корневого элемента. -
reactNode: «Узел React», использованный для отрисовки существующего HTML. Обычно это фрагмент JSX, например<App />, отрисованный с помощью методаReactDOM Server, такого какrenderToPipeableStream(<App />). -
необязательный
options: объект с параметрами для этого корня React.-
необязательный
onCaughtError: функция обратного вызова, вызываемая, когда React перехватывает ошибку в границе ошибок. Вызывается сerror, перехваченной границей ошибок, и объектомerrorInfo, содержащимcomponentStack. -
необязательный
onUncaughtError: функция обратного вызова, вызываемая, когда возникает ошибка, не перехваченная границей ошибок. Вызывается сerror, и объектомerrorInfo, содержащимcomponentStack. -
необязательный
onRecoverableError: функция обратного вызова, вызываемая, когда React автоматически восстанавливается после ошибок. Вызывается сerror, которые выдаёт React, и объектомerrorInfo, содержащимcomponentStack. Некоторые ошибки, допускающие восстановление, могут включать исходную причину ошибки вerror.cause. -
необязательный
identifierPrefix: строковый префикс, который React использует для идентификаторов, создаваемыхuseId. Полезен, чтобы избежать конфликтов при использовании нескольких корней на одной странице. Должен совпадать с префиксом, использованным на сервере.
-
необязательный
Возвращаемое значение
hydrateRoot возвращает объект с двумя методами: render и unmount.
Особенности
-
hydrateRoot()предполагает, что отрисованное содержимое полностью совпадает с содержимым, отрисованным на сервере. Несоответствия следует считать ошибками и исправлять. - В режиме разработки React предупреждает о несоответствиях во время гидратации. Нет гарантии, что различия в атрибутах будут исправлены при наличии несоответствий. Это важно для производительности: в большинстве приложений несоответствия редки, поэтому проверка всей разметки была бы чрезмерно затратной.
- В вашем приложении, скорее всего, будет только один вызов
hydrateRoot. Если вы используете фреймворк, возможно, он выполнит этот вызов за вас. - Если приложение отображается на клиенте и HTML ещё не был отрисован, использовать
hydrateRoot()нельзя. Вместо этого используйтеcreateRoot().
root.render(reactNode)
Вызовите root.render, чтобы обновить компонент React внутри гидратированного корня React для элемента DOM браузера.
root.render(<App />); React обновит <App /> в гидратированном root.
Ниже приведено больше примеров.
Параметры
-
reactNode: «Узел React», который нужно обновить. Обычно это фрагмент JSX, например<App />, но также можно передать элемент React, созданный с помощьюcreateElement(), строку, число,nullилиundefined.
Возвращаемое значение
root.render возвращает undefined.
Особенности
- Если вызвать
root.renderдо завершения гидратации корня, React очистит существующее HTML-содержимое, отрисованное на сервере, и переключит весь корень на клиентскую отрисовку.
root.unmount()
Вызовите root.unmount, чтобы уничтожить отрисованное дерево внутри корня React.
root.unmount(); В приложении, полностью созданном с помощью React, обычно нет вызовов root.unmount.
Этот метод полезен главным образом в том случае, если узел DOM корня React (или любой из его предков) может быть удалён из DOM другим кодом. Например, представьте панель вкладок jQuery, которая удаляет неактивные вкладки из DOM. Если вкладку удалить, всё её содержимое (включая расположенные внутри корни React) также будет удалено из DOM. Нужно сообщить React, чтобы он «перестал» управлять содержимым удалённого корня, вызвав root.unmount. Иначе компоненты внутри удалённого корня не выполнят очистку и не освободят такие ресурсы, как подписки.
Вызов root.unmount размонтирует все компоненты в корне и «отсоединит» React от корневого узла DOM, в том числе удалит обработчики событий и состояние дерева.
Параметры
root.unmount не принимает параметров.
Возвращаемое значение
root.unmount возвращает undefined.
Особенности
-
Вызов
root.unmountразмонтирует все компоненты дерева и «отсоединит» React от корневого узла DOM. -
После вызова
root.unmountнельзя снова вызыватьroot.renderдля этого корня. Попытка вызватьroot.renderдля размонтированного корня приведёт к ошибке «Cannot update an unmounted root».
Использование
Гидратация HTML, отрисованного на сервере
Если HTML вашего приложения был создан с помощью react-dom/server, необходимо выполнить его гидратацию на клиенте.
import { hydrateRoot } from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />); Это позволит выполнить гидратацию серверного HTML внутри узла DOM браузера с помощью компонента React вашего приложения. Обычно это делается один раз при запуске. Если вы используете фреймворк, возможно, он выполнит это за вас автоматически.
Чтобы выполнить гидратацию приложения, React «подключит» логику ваших компонентов к исходному HTML, созданному сервером. Гидратация превращает исходный HTML-снимок с сервера в полноценное интерактивное приложение, работающее в браузере.
import './styles.css'; import { hydrateRoot } from 'react-dom/client'; import App from './App.js'; hydrateRoot( document.getElementById('root'), <App /> );
Вам не нужно повторно вызывать hydrateRoot или вызывать его в других местах. С этого момента React будет управлять DOM вашего приложения. Чтобы обновлять интерфейс, компоненты будут вместо этого использовать состояние.
Подводный камень
Дерево React, переданное в hydrateRoot, должно создавать тот же результат, что и на сервере.
Это важно для удобства пользователей. Некоторое время пользователь будет видеть HTML, созданный сервером, пока не загрузится JavaScript-код. Серверная отрисовка создаёт иллюзию более быстрой загрузки приложения, показывая HTML-снимок его результата. Внезапное отображение другого содержимого разрушает эту иллюзию. Поэтому результат серверной отрисовки должен совпадать с результатом первоначальной отрисовки на клиенте.
К наиболее распространённым причинам ошибок гидратации относятся:
- Лишние пробелы (например, символы новой строки) вокруг HTML, созданного React, внутри корневого узла.
- Использование проверок, например
typeof window !== 'undefined', в логике отрисовки. - Использование API, доступных только в браузере, например
window.matchMedia, в логике отрисовки. - Отрисовка разных данных на сервере и клиенте.
React устраняет некоторые ошибки гидратации, но их необходимо исправлять, как и любые другие ошибки. В лучшем случае они приведут к замедлению работы; в худшем — обработчики событий могут быть привязаны не к тем элементам.
Гидратация всего документа
Приложения, полностью созданные с помощью React, могут отрисовывать весь документ как JSX, включая тег <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>My app</title>
</head>
<body>
<Router />
</body>
</html>
);
} Чтобы выполнить гидратацию всего документа, передайте глобальный объект document в качестве первого аргумента в hydrateRoot:
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App />); Подавление неизбежных ошибок несоответствия при гидратации
Если атрибут или текстовое содержимое отдельного элемента неизбежно различается на сервере и клиенте (например, временная метка), можно отключить предупреждение о несоответствии при гидратации.
Чтобы отключить предупреждения о гидратации для элемента, добавьте suppressHydrationWarning={true}:
export default function App() { return ( <h1 suppressHydrationWarning={true}> Current Date: {new Date().toLocaleDateString()} </h1> ); }
Это работает только на один уровень вглубь и предназначено для исключительных случаев. Не злоупотребляйте этим. React не будет пытаться исправить несовпадающее текстовое содержимое.
Обработка различающегося содержимого на клиенте и сервере
Если вам намеренно нужно отрисовать разное содержимое на сервере и клиенте, можно выполнить отрисовку в два прохода. Компоненты, которые должны отображать разное содержимое на клиенте, могут считывать переменную состояния, например isClient, которую можно установить в true с помощью эффекта:
import { useState, useEffect } from "react"; export default function App() { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true); }, []); return ( <h1> {isClient ? 'Is Client' : 'Is Server'} </h1> ); }
Таким образом, при первоначальной отрисовке будет создано то же содержимое, что и на сервере, и несоответствий не возникнет. Однако сразу после гидратации синхронно выполнится дополнительный проход.
Подводный камень
Этот подход замедляет гидратацию, поскольку компоненты приходится отрисовывать дважды. Учитывайте удобство пользователей при медленном подключении. JavaScript-код может загрузиться значительно позже первоначально отрисованного HTML, поэтому немедленное отображение другого интерфейса после гидратации может показаться пользователю резким.
Обновление гидратированного корневого компонента
После завершения гидратации корня можно вызвать root.render, чтобы обновить корневой компонент React. В отличие от createRoot, обычно в этом нет необходимости, поскольку исходное содержимое уже было отрисовано как HTML.
Если после гидратации вызвать root.render и структура дерева компонентов совпадает с ранее отрисованной, React сохранит состояние. Обратите внимание: в поле ввода можно печатать, а значит, обновления, выполняемые каждую секунду повторными вызовами render в этом примере, не приводят к потере состояния:
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);
Вызов root.render для гидратированного корня — редкость. Обычно вместо этого обновляют состояние внутри одного из компонентов.
Регистрация ошибок в рабочей среде
По умолчанию React записывает все ошибки в консоль. Чтобы реализовать собственную систему отчётов об ошибках, можно передать необязательные параметры обработчиков ошибок onUncaughtError, onCaughtError и onRecoverableError:
import { hydrateRoot } from "react-dom/client";
import App from "./App.js";
import { reportCaughtError } from "./reportError";
const container = document.getElementById("root");
const root = hydrateRoot(container, <App />, {
onCaughtError: (error, errorInfo) => {
if (error.message !== "Known error") {
reportCaughtError({
error,
componentStack: errorInfo.componentStack,
});
}
},
}); Параметр onCaughtError — это функция, вызываемая с двумя аргументами:
- ошибка, которая была вызвана.
- Объект errorInfo, содержащий componentStack ошибки.
Вместе с onUncaughtError и onRecoverableError вы можете реализовать собственную систему отчётов об ошибках:
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import { onCaughtErrorProd, onRecoverableErrorProd, onUncaughtErrorProd, } from "./reportError"; const container = document.getElementById("root"); hydrateRoot(container, <App />, { // Keep in mind to remove these options in development to leverage // React's default handlers or implement your own overlay for development. // The handlers are only specfied unconditionally here for demonstration purposes. onCaughtError: onCaughtErrorProd, onRecoverableError: onRecoverableErrorProd, onUncaughtError: onUncaughtErrorProd, });
Устранение неполадок
Появилась ошибка: «В root.render передан второй аргумент»
Распространённая ошибка — передать параметры для hydrateRoot в root.render(...):
Чтобы исправить это, передайте параметры root в hydrateRoot(...), а не в root.render(...):
// 🚩 Wrong: root.render only takes one argument.
root.render(App, {onUncaughtError});
// ✅ Correct: pass options to createRoot.
const root = hydrateRoot(container, <App />, {onUncaughtError});
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/reference/react-dom/client/hydrateRoot