Индикация процесса загрузки

При работе с асинхронными источниками данных компонент выбора становится зависимым от сетевых задержек и времени ответа API. В подобных условиях индикация загрузки выполняет не декоративную, а функциональную роль: она сигнализирует о состоянии запроса, предотвращает повторные действия пользователя и формирует предсказуемое поведение интерфейса.

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


Встроенные механизмы управления загрузкой

В основе асинхронной модели лежит функция load(query, callback), которая вызывается при необходимости получения данных извне. В момент запуска запроса компонент автоматически переводится в состояние загрузки.

Ключевые особенности:

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

Механизм загрузки не требует ручного переключения флагов в базовой конфигурации, однако предоставляет точки расширения для кастомизации поведения.


Состояния загрузки и CSS-классы

Во время выполнения запроса основной контейнер компонента получает CSS-класс:

  • loading — активная загрузка данных

Этот класс применяется к корневому элементу селекта и может быть использован для стилизации индикаторов.

Пример типичной структуры DOM в момент загрузки:

  • контейнер получает класс loading
  • список опций может быть пустым или временно скрытым
  • отображается placeholder состояния ожидания (если задан)

Использование этого состояния позволяет:

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

Загрузка данных через load()

Основной точкой интеграции является функция load, определяемая при инициализации компонента:

new TomSelect("#select", {
  load: function(query, callback) {
    fetch(`/api/items?q=${encodeURIComponent(query)}`)
      .then(response => response.json())
      .then(data => callback(data.items))
      .catch(() => callback([]));
  }
});

Параметры функции:

  • query — текущий ввод пользователя
  • callback — функция завершения загрузки

Особенности поведения:

  • callback([]) завершает состояние загрузки без результатов
  • повторный вызов load игнорируется при активном запросе
  • отсутствие вызова callback блокирует интерфейс

Управление частотой запросов

Для предотвращения избыточных обращений к серверу используется параметр:

  • loadThrottle

Он определяет минимальный интервал между запросами в миллисекундах.

Пример конфигурации:

new TomSelect("#select", {
  loadThrottle: 300,
  load: function(query, callback) {
    api.search(query).then(callback);
  }
});

Поведение:

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

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

В некоторых сценариях требуется загрузка данных до ввода пользователя. Для этого используется параметр:

  • preload

Возможные значения:

  • true — загрузка при инициализации
  • "focus" — загрузка при фокусе на поле
  • false — отключено

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


Кастомизация индикатора загрузки

Встроенный индикатор может быть заменён через систему рендеринга.

Пример кастомного отображения:

new TomSelect("#select", {
  load: function(query, callback) {
    api.search(query).then(callback);
  },
  render: {
    loading: function() {
      return `<div class="ts-loading-indicator">
                <span class="spinner"></span>
              </div>`;
    }
  }
});

Особенности кастомного рендера:

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

Интеграция с удалёнными API

При работе с серверными источниками важно учитывать структуру данных и задержки сети.

Типовая схема:

  1. ввод пользователя
  2. debounce через loadThrottle
  3. вызов load
  4. переход в состояние loading
  5. получение ответа
  6. обновление списка опций
  7. снятие состояния loading

Дополнительно могут использоваться:

  • пагинация результатов
  • ограничение числа элементов (maxOptions)
  • серверная фильтрация

Обработка ошибок загрузки

Ошибки сетевых запросов не должны приводить к зависанию интерфейса. Обязательное правило — завершение callback даже при ошибке.

Пример обработки:

load: function(query, callback) {
  fetch(`/api/items?q=${query}`)
    .then(r => r.json())
    .then(data => callback(data.items))
    .catch(err => {
      console.error(err);
      callback([]);
    });
}

Поведение при ошибке:

  • состояние loading снимается автоматически
  • список очищается или остаётся прежним
  • пользователь может повторить ввод

Управление повторными запросами и гонками состояний

При высокой скорости ввода возможны ситуации, когда ответы приходят в неправильном порядке. Для предотвращения этого применяются стратегии:

  • игнорирование устаревших запросов
  • сравнение query перед применением результата
  • использование локального токена запроса

Пример защиты:

let lastQuery = "";

load: function(query, callback) {
  lastQuery = query;

  fetch(`/api?q=${query}`)
    .then(r => r.json())
    .then(data => {
      if (query === lastQuery) {
        callback(data.items);
      }
    });
}

Кэширование результатов загрузки

Для уменьшения нагрузки на сервер используется кэширование запросов.

Подходы:

  • in-memory cache по ключу query
  • нормализация строки запроса
  • хранение ограниченного числа записей (LRU-подход)

Пример:

const cache = new Map();

load: function(query, callback) {
  if (cache.has(query)) {
    callback(cache.get(query));
    return;
  }

  fetch(`/api?q=${query}`)
    .then(r => r.json())
    .then(data => {
      cache.set(query, data.items);
      callback(data.items);
    });
}

Влияние состояния загрузки на взаимодействие пользователя

Во время активной загрузки компонент может:

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

Поведение зависит от конфигурации и реализации load, но базовый механизм гарантирует согласованность UI-состояния.


Стилизация состояния загрузки через CSS

Стандартный класс loading позволяет полностью контролировать внешний вид:

.ts-wrapper.loading {
  opacity: 0.8;
}

.ts-wrapper.loading .ts-dropdown {
  pointer-events: none;
}

Дополнительные техники:

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

Оптимизация UX при частых запросах

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

Используются подходы:

  • увеличение loadThrottle
  • предиктивная подгрузка популярных запросов
  • отображение «последних результатов» при пустом вводе
  • ограничение числа перерисовок dropdown

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


Связь загрузки с жизненным циклом компонента

Состояние загрузки тесно связано с внутренним жизненным циклом:

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

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