Состояния загрузки

Haunted — это библиотека для создания веб-компонентов с использованием реактивного подхода на базе Hooks, аналогично React, но с упором на нативные веб-компоненты. Управление состоянием загрузки является одной из ключевых задач при работе с асинхронными данными и пользовательским интерфейсом.

Основы состояния загрузки

Состояние загрузки обычно представляется булевым флагом, который показывает, выполняется ли в данный момент асинхронная операция. В Haunted это состояние удобно хранить с помощью хука useState.

import { html, component, useState, useEffect } from 'haunted';

function DataLoader() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(json => {
        setData(json);
        setLoading(false);
      })
      .catch(err => {
        setError(err);
        setLoading(false);
      });
  }, []);

  if (loading) return html`<p>Загрузка...</p>`;
  if (error) return html`<p>Ошибка: ${error.message}</p>`;
  return html`<pre>${JSON.stringify(data, null, 2)}</pre>`;
}

customElements.define('data-loader', component(DataLoader));

Ключевые моменты:

  • loading определяет момент отображения индикатора загрузки.
  • Состояние обновляется после завершения асинхронной операции или при возникновении ошибки.
  • Использование useEffect обеспечивает запуск асинхронного запроса только один раз при монтировании компонента.

Динамическая визуализация состояния

Для улучшения UX состояние загрузки часто сопровождается анимацией или скелетонами (skeleton screens). В Haunted можно динамически менять разметку в зависимости от состояния:

function SkeletonLoader() {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const timer = setTimeout(() => setLoading(false), 3000);
    return () => clearTimeout(timer);
  }, []);

  return html`
    ${loading
      ? html`<div class="skeleton">Загрузка контента...</div>`
      : html`<div>Контент загружен</div>`}
  `;
}

customElements.define('skeleton-loader', component(SkeletonLoader));

Особенности:

  • Компонент автоматически обновляет DOM при изменении состояния.
  • В Haunted шаблон возвращается через html и полностью реактивен.

Асинхронные функции внутри хуков

Иногда требуется использовать асинхронные функции напрямую в хуках. В Haunted нельзя передавать асинхронную функцию в useEffect напрямую, но можно создать вложенную функцию:

useEffect(() => {
  async function fetchData() {
    try {
      setLoading(true);
      const response = await fetch('https://api.example.com/data');
      const result = await response.json();
      setData(result);
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  }
  fetchData();
}, []);

Преимущества такого подхода:

  • Легкая обработка ошибок через try/catch.
  • Четкое управление состояниями loading и error.
  • Поддержка нескольких последовательных асинхронных вызовов.

Комбинирование состояний

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

function PaginatedLoader({ page }) {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetch(`https://api.example.com/data?page=${page}`)
      .then(res => res.json())
      .then(json => setData(json))
      .finally(() => setLoading(false));
  }, [page]);

  return html`
    ${loading
      ? html`<p>Загрузка страницы ${page}...</p>`
      : html`<ul>${data.map(item => html`<li>${item.name}</li>`)}</ul>`}
  `;
}

customElements.define('paginated-loader', component(PaginatedLoader));

Здесь ключевой момент: состояние загрузки привязано к параметру page. Haunted автоматически перерендеривает компонент при изменении зависимостей хука useEffect.

Рекомендации по организации состояния загрузки

  • Минимизировать множественные состояния: лучше объединять связанные состояния в один объект, чтобы избежать лишних перерендеров.
  • Явно обрабатывать ошибки: отдельное состояние error облегчает отладку и отображение пользовательских сообщений.
  • Отделять UI от логики: логика загрузки и визуальные индикаторы должны быть раздельными, чтобы компоненты оставались переиспользуемыми.
  • Использовать индикаторы прогресса для длинных запросов, чтобы пользователь видел динамику процесса.

Состояния загрузки в Haunted создают плавный пользовательский опыт и позволяют контролировать асинхронные процессы в реактивной модели веб-компонентов, обеспечивая полную управляемость визуального состояния интерфейса.