Гидратация на клиенте

Клиентская гидратация в контексте Tom Select представляет собой процесс восстановления интерактивного состояния компонента на стороне браузера после серверного рендеринга исходного <select> элемента или его HTML-аналога. Основная задача заключается в том, чтобы синхронизировать уже существующую DOM-структуру с внутренним состоянием экземпляра Tom Select без потери выбранных значений, пользовательских опций и конфигурации, сформированной на сервере.

Ключевая особенность гидратации заключается в том, что библиотека не создает интерфейс «с нуля», а «оживляет» уже существующую разметку, минимизируя количество операций над DOM и предотвращая рассинхронизацию между серверным и клиентским состоянием.


Базовая модель SSR и роль Tom Select

При серверном рендеринге формируется стандартный HTML:

  • <select> с option
  • предустановленные selected значения
  • возможные disabled состояния
  • атрибуты data-*, содержащие метаданные

Tom Select на клиенте должен:

  • распознать исходный элемент
  • прочитать его текущее состояние
  • построить внутреннюю модель данных
  • синхронизировать UI с DOM

Главная сложность возникает в том, что сервер не знает о внутреннем состоянии Tom Select, а клиент должен корректно интерпретировать уже готовый HTML как источник истины.


Этапы гидратации экземпляра

Захват исходного элемента

Первым шагом является инициализация на уже существующем DOM-узле:

const el = document.querySelector('select');
const control = new TomSelect(el);

На этом этапе библиотека:

  • фиксирует ссылку на исходный элемент
  • анализирует его опции
  • определяет текущие выбранные значения

Чтение серверного состояния

Tom Select извлекает данные из DOM:

  • option.value
  • option.textContent
  • option.selected
  • option.disabled

Дополнительно учитываются пользовательские атрибуты:

  • data-* для кастомных полей
  • optgroup структуры
  • вложенные группы и иерархия

Серверное состояние становится базой для построения внутреннего массива items.


Построение внутренней модели данных

После чтения DOM формируется структура:

  • список элементов (items)
  • индексированный набор опций (options)
  • активные значения (activeItems)

Важно, что Tom Select не копирует DOM напрямую, а строит абстракцию поверх него. Это позволяет:

  • ускорить фильтрацию
  • упростить поиск
  • минимизировать обращения к DOM

Проблема повторной инициализации

Одной из критических ошибок является повторный вызов конструктора на уже гидратированном элементе.

Без защиты возможны:

  • дублирование UI контейнеров
  • утрата обработчиков событий
  • рассинхронизация значений

Tom Select решает это через внутреннюю привязку экземпляра к DOM-узлу:

if (el.tomselect) {
  el.tomselect.destroy();
}

Или через проверку наличия активного инстанса.


Сохранение выбранных значений

При гидратации важно сохранить состояние selected:

  • одиночный выбор (single)
  • множественный выбор (multiple)

Алгоритм:

  1. прочитать selected у <option>
  2. сопоставить с внутренним options
  3. перенести в items
  4. синхронизировать UI

Особое внимание уделяется случаям, когда значения отсутствуют в списке опций (например, сервер передал устаревший HTML).


Синхронизация атрибутов и кастомных полей

Tom Select поддерживает расширенные поля данных:

  • labelField
  • valueField
  • searchField

При гидратации:

  • значения извлекаются из data-*
  • формируется расширенный объект опции
  • происходит нормализация данных

Пример структуры:

{
  value: "42",
  text: "Option text",
  group: "Category A",
  custom: "meta"
}

Гидратация с optgroup

При наличии группировки:

<optgroup label="Fruits">
  <option value="1">Apple</option>
</optgroup>

Tom Select строит иерархию:

  • группы становятся контейнерами
  • опции связываются с группами
  • сохраняется порядок отображения

Важно, что порядок DOM становится источником истины, если не задан sortField.


Обработка асинхронных источников данных

В случаях, когда данные подгружаются динамически, гидратация усложняется:

  • часть опций уже есть в DOM
  • часть загружается через load callback

Стратегия:

  • сначала гидратируется DOM
  • затем выполняется merge с удаленным источником
  • конфликтующие значения разрешаются через приоритет локального состояния

Режимы создания UI при гидратации

Tom Select может работать в нескольких режимах:

Полная переработка DOM

  • оригинальный <select> скрывается
  • создается новый контейнер UI
  • все события переназначаются

Частичная гидратация

  • сохраняется исходный <select>
  • UI накладывается поверх
  • минимизируются DOM-операции

Синхронизация событий после гидратации

После восстановления состояния активируются обработчики:

  • change
  • item_add
  • item_remove
  • dropdown_open
  • dropdown_close

Критично, что события не должны срабатывать во время первичной гидратации, иначе возможны ложные side effects.

Используется флаг инициализации:

this.isInitializing = true;
// гидратация
this.isInitializing = false;

Гидратация в условиях SPA

В SPA-архитектурах (React, Vue, Angular без прямой интеграции):

  • DOM может пересоздаваться
  • экземпляры Tom Select нужно уничтожать перед повторной инициализацией
  • состояние часто хранится вне компонента

Основная проблема — дублирование инстансов при повторном рендере компонента.

Решение:

  • явный destroy()
  • хранение ссылок на экземпляры
  • контроль жизненного цикла DOM

Типичные ошибки при клиентской гидратации

Несовпадение server/client HTML

Если сервер передал:

<option value="1" selected>One</option>

а клиент ожидает другое состояние, возникает конфликт при инициализации.


Потеря выбранных значений

Причины:

  • опции отсутствуют в DOM
  • неправильный valueField
  • асинхронная загрузка перекрывает состояние

Дублирование UI

Возникает при повторной инициализации без очистки предыдущего экземпляра.


Несогласованность групп

При изменении optgroup на клиенте без синхронизации с сервером нарушается порядок и вложенность.


Стратегии устойчивой гидратации

Идемпотентная инициализация

Повторная инициализация должна приводить к одинаковому результату:

  • одинаковые items
  • одинаковый UI
  • одинаковый порядок

Использование data-атрибутов как источника истины

DOM дополняется:

<select data-preload="true" data-initial-value="2"></select>

Это позволяет восстанавливать состояние даже при частично пустом <select>.


Контроль диффа состояния

При гидратации применяется логика сравнения:

  • DOM значения
  • внутренний state
  • серверные данные

Различия приводят к приоритету одного источника в зависимости от конфигурации.


Взаимодействие с формами

При использовании в <form> важно учитывать:

  • синхронизацию скрытого input
  • корректную сериализацию
  • обновление значения при изменениях UI

Tom Select поддерживает автоматическое обновление исходного <select> как источника данных формы, что критично при отправке POST/GET запросов.


Производительность гидратации

Оптимизация достигается за счет:

  • минимального числа DOM-операций
  • батчинга обновлений
  • кеширования опций
  • ленивого рендера dropdown

Особенно важно при больших списках (1000+ элементов), где полная переработка DOM становится дорогостоящей операцией.


Поведение при частичной гидратации и фильтрации

При включенном поиске:

  • сервер может передавать усеченный список
  • клиент расширяет его через load
  • фильтр работает поверх уже гидратированных данных

Состояние поиска не сохраняется автоматически и должно быть восстановлено отдельно при необходимости.


Интеграция с кастомными шаблонами

Если используются кастомные render функции:

  • гидратация должна учитывать шаблон
  • HTML может отличаться от стандартного
  • данные должны быть полностью восстановлены до применения шаблонов

Это предотвращает визуальные артефакты при первичном рендере.


Управление жизненным циклом экземпляра

Корректная гидратация невозможна без управления состоянием:

  • создание
  • обновление
  • уничтожение
  • повторная инициализация

Каждый цикл должен гарантировать чистое состояние без утечек памяти и дублирования обработчиков событий.