Розбіжність HTML
Контекст: що таке гідрація
Після SSR браузер отримує готовий HTML. Клієнтський JavaScript (React, Vue та ін.) потім виконує той самий рендер, щоб "прив'язати" логіку і події до вже існуючого DOM. Цей процес - hydration.
Щоб гідрація пройшла успішно:
Клієнтський віртуальний DOM повинен точно збігатися з HTML, який згенерував сервер.
Що відбувається при розбіжності
1. React намагається "зіставити" DOM
React порівнює наявний HTML (з сервера) з тим, що він очікує при клієнтському рендерингу.
Якщо відмінності незначні (наприклад, атрибут class або текст всередині елемента), React може спробувати мовчки виправити DOM.
Якщо відмінності серйозні - React виведе попередження або навіть перерендерить частину дерева.
2. React видає помилку в консоль
Ти побачиш повідомлення на кшталт:
Warning: Text content did not match. Server: "42" Client: "43"або
Warning: Hydration failed because the initial UI does not match what was rendered on the server.Це означає: React очікував одне дерево, а отримав інше.
3. Можливі наслідки
| Ситуація | Що станеться |
|---|---|
| Невеликі відмінності (текст, атрибут) | React тихо виправить DOM, але це може викликати "reflow" або блимання інтерфейсу |
| Суттєві відмінності (різна структура) | React видалить старий HTML і перерендерить компонент заново |
| Масові розбіжності | Втрата стану, подвійне виконання ефектів, помилки логіки |
| У строгому режимі (React 18 StrictMode) | React видає попередження і може скинути все дерево |
Приклад
Сервер віддав:
<div id="root">
<p>Сьогодні: 17 жовтня</p>
</div>Клієнт відрендерив:
<div id="root">
<p>Сьогодні: 18 жовтня</p>
</div>React виявить розбіжність і покаже:
Warning: Text content did not match. Server: "Сьогодні: 17 жовтня" Client: "Сьогодні: 18 жовтня"Користувач побачить коротке "блимання" контенту - React замінить текст після гідрації.
Основні причини помилок гідрації
1. Використання "нестабільних" значень
Math.random()Date.now()new Date()- UUID / випадкові ключі
- випадкові id (наприклад,
id={Math.random()})
Рішення: Генеруй їх на сервері заздалегідь і передавай через props.
2. Код, залежний від браузера
На сервері немає window, document, navigator, localStorage тощо.
Неправильно:
const width = window.innerWidth; // Помилка при SSRПравильно:
const [width, setWidth] = useState(0);
useEffect(() => setWidth(window.innerWidth), []);3. Умовний рендеринг, залежний від середовища
Якщо компонент рендериться по-різному залежно від:
- розміру екрана,
- користувацької сесії,
- локалі,
- cookie / theme mode.
Приклад:
{typeof window !== "undefined" && <Sidebar />}На сервері window немає → Sidebar не відрендериться.
На клієнті window є → Sidebar з'явиться → mismatch.
Рішення:
Рендерити з тими самими умовами або використовувати useEffect для клієнтського коду.
4. Розбіжність даних між сервером і клієнтом
Якщо SSR довантажує одні дані, а клієнт повторно запитує інші → розбіжність HTML.
Рішення:
- Передавати
initialDataз SSR на клієнт через__INITIAL_STATE__абоpageProps. - Використовувати однакову логіку завантаження даних.
Як React реагує (React 18+)
| Версія | Поведінка |
|---|---|
| React 17 і нижче | Мовчки виправляв відмінності (часто спричиняв баги) |
| React 18 | Явно попереджає в консолі і може скинути DOM |
| React 18 (StrictMode) | Перевіряє подвійним рендером, щоб виявити розбіжності |
Як уникнути mismatches
- Використовуй детерміновані дані при SSR.
- Не рендери елементи, залежні від браузера, до гідрації.
- Передавай на клієнт усі initial props/data.
- Перевіряй SSR-рендер через
ReactDOMServer.renderToString()локально. - Вмикай
StrictMode- він допомагає виявити помилки гідрації.
Висновок
Якщо HTML, згенерований на сервері, не збігається з клієнтським:
- React видає попередження або помилку гідрації.
- Може відбутися перерендер, втрата стану або блимання інтерфейсу.
- Це часто пов'язано з недетермінованим рендерингом (дати, рандом, window тощо).
Ідеальна SSR-гідрація = той самий HTML, незалежно від того, чи був він згенерований на сервері, чи на клієнті.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.