Реинициализация

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


Базовая модель жизненного цикла экземпляра

Каждый экземпляр Tom Select создаётся поверх стандартного <select> элемента и формирует собственную обёртку с дополнительной структурой DOM:

  • скрывает оригинальный <select>
  • создаёт контейнер управления
  • навешивает обработчики событий
  • инициализирует внутренний state (значения, опции, поиск)

При этом экземпляр хранится в памяти и привязывается к DOM-элементу через ссылку, обычно доступную через element.tomselect.


Причины необходимости реинициализации

Реинициализация требуется в следующих случаях:

  • динамическая замена <select> в SPA-логике
  • подгрузка нового набора options с сервера
  • изменение конфигурации, несовместимое с текущим экземпляром
  • повторное применение инициализации после удаления компонента из DOM
  • клонирование формы или модального окна с селектами

Типичная проблема — повторная инициализация без очистки предыдущего экземпляра, приводящая к дублированию событий и конфликту DOM-узлов.


Корректное уничтожение экземпляра

Перед созданием нового экземпляра требуется полное удаление текущего состояния через метод destroy().

const select = document.querySelector('select');

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

Метод destroy() выполняет:

  • удаление всех обработчиков событий
  • восстановление исходного состояния <select>
  • удаление созданной DOM-обёртки
  • очистку внутреннего состояния экземпляра

После этого DOM-элемент возвращается к состоянию, близкому к исходному, что позволяет безопасно выполнять повторную инициализацию.


Базовая реинициализация

После очистки экземпляра создаётся новый объект Tom Select:

new TomSelect('#select', {
    create: true,
    sortField: 'text'
});

Ключевым моментом является гарантия отсутствия активного экземпляра, иначе библиотека не выполняет полную пересборку структуры и начинает работать поверх существующих элементов.


Проверка наличия активного экземпляра

Для предотвращения конфликтов используется проверка:

const el = document.querySelector('#select');

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

new TomSelect(el, {
    maxItems: 5
});

Такой подход исключает утечку обработчиков и двойную привязку логики.


Реинициализация с заменой DOM-элемента

При динамической генерации формы старый <select> может быть заменён новым узлом. В этом случае важно учитывать, что ссылка на экземпляр теряется.

const wrapper = document.querySelector('.wrapper');

wrapper.innerHTML = `
    <select id="select">
        <option value="1">A</option>
        <option value="2">B</option>
    </select>
`;

new TomSelect('#select');

В подобных сценариях повторная инициализация не требует destroy(), так как предыдущий DOM уже удалён вместе с экземпляром.


Реинициализация с изменением конфигурации

Некоторые параметры Tom Select не поддерживают динамическое изменение без пересоздания экземпляра. К таким относятся:

  • options
  • plugins
  • render
  • load

При необходимости изменения конфигурации используется полный цикл пересоздания:

function rebuildSelect(el, config) {
    if (el.tomselect) {
        el.tomselect.destroy();
    }

    return new TomSelect(el, config);
}

Работа с состоянием перед уничтожением

Перед вызовом destroy() часто требуется сохранить текущее состояние:

const instance = select.tomselect;

const value = instance.getValue();
const options = instance.options;

После реинициализации значения можно восстановить:

const newInstance = new TomSelect(select, config);

newInstance.setValue(value);

Это особенно важно при асинхронной загрузке данных.


Частые проблемы при реинициализации

Дублирование событий

Возникает при повторной инициализации без destroy(). Симптомы:

  • двойной вызов onChange
  • неконтролируемое добавление элементов
  • ускоренное выполнение событий поиска

Утечка DOM-узлов

При многократной инициализации без очистки остаются скрытые контейнеры:

  • .ts-wrapper
  • .ts-dropdown

Это приводит к росту DOM и падению производительности.


Несовпадение состояния и DOM

После частичной модификации <select> вручную Tom Select может хранить устаревший state. В таких случаях требуется полное пересоздание экземпляра вместо попытки обновления.


Использование refreshOptions и частичная альтернатива реинициализации

В ряде случаев полная реинициализация избыточна. Tom Select предоставляет методы обновления:

select.tomselect.clearOptions();
select.tomselect.addOption({ value: 'x', text: 'New' });
select.tomselect.refreshOptions(false);

Однако такие методы не заменяют destroy(), если изменяется фундаментальная структура.


Реинициализация в динамических интерфейсах

В интерфейсах с модальными окнами и табами часто применяется повторная инициализация при каждом открытии:

function initModalSelect() {
    const el = document.querySelector('#modal-select');

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

    new TomSelect(el, {
        dropdownParent: 'body'
    });
}

Особое внимание уделяется dropdownParent, так как перенос dropdown в body предотвращает обрезание в overflow-контейнерах.


Реинициализация в SPA-архитектуре

В SPA-фреймворках (React, Vue, Svelte без обёрток или при интеграции через ref) реинициализация часто связана с жизненным циклом компонентов.

Типичный паттерн:

  • mount → инициализация
  • update props → проверка необходимости destroy + recreate
  • unmount → destroy
useEffect(() => {
    const ts = new TomSelect(ref.current, config);

    return () => {
        ts.destroy();
    };
}, [config]);

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

Если создать новый экземпляр поверх старого:

new TomSelect('#select');
new TomSelect('#select');

возникают:

  • конфликт DOM-обёрток
  • неконсистентное состояние выбора
  • дублирование dropdown
  • ошибки обработки событий

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


Управление экземпляром через ссылку DOM

Tom Select сохраняет ссылку на себя в DOM-узле:

const ts = document.querySelector('#select').tomselect;

Это позволяет:

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

Оптимизированная стратегия реинициализации

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

function safeInit(selector, config) {
    const el = document.querySelector(selector);

    if (!el) return;

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

    el.tomselect = new TomSelect(el, config);

    return el.tomselect;
}

Такой подход предотвращает накопление экземпляров и гарантирует консистентность поведения даже при частых перерендерах интерфейса.