Higher-order components

Higher-Order Components (HOC) представляют собой функцию, которая принимает компонент и возвращает новый компонент с расширенной функциональностью. В экосистеме Svelte и SvelteKit подход к HOC отличается от привычных практик в React, но суть остаётся прежней: повторное использование логики и композиция поведения компонентов.


Принцип работы HOC

HOC не изменяет исходный компонент напрямую. Вместо этого он оборачивает его в другой компонент, добавляя дополнительные свойства, методы или обработку событий. В Svelte это достигается через:

  1. Передачу пропсов в дочерний компонент.
  2. Использование слотов для гибкой композиции.
  3. Инкапсуляцию побочных эффектов с помощью onMount и реактивных переменных.

Пример базового HOC для логирования пропсов:

<!-- withLogger.svelte -->
<script>
  export let WrappedComponent;
  export let props;

  import { onMount } from 'svelte';

  onMount(() => {
    console.log('Props:', props);
  });
</script>

<svelte:component this={WrappedComponent} {...props} />

Использование:

<script>
  import Button from './Button.svelte';
  import WithLogger from './withLogger.svelte';
</script>

<WithLogger WrappedComponent={Button} props={{ text: 'Click me' }} />

Здесь WithLogger добавляет поведение логирования к любому компоненту без изменения его исходного кода.


Паттерны применения HOC

1. Управление состоянием

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

<!-- withCounter.svelte -->
<script>
  export let WrappedComponent;
  let count = 0;

  const increment = () => count += 1;
</script>

<svelte:component this={WrappedComponent} {count} {increment} />

Использование позволяет любому компоненту получать счётчик и функцию увеличения без дублирования логики.

2. Авторизация и контроль доступа

HOC удобно применять для проверки прав пользователя перед рендерингом компонента:

<script>
  export let WrappedComponent;
  export let user;

  let authorized = false;

  $: authorized = user?.role === 'admin';
</script>

{#if authorized}
  <svelte:component this={WrappedComponent} />
{:else}
  <p>Доступ запрещён</p>
{/if}

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

3. Обработка API и данных

HOC может инкапсулировать логику работы с API, предоставляя компоненту уже готовые данные:

<script>
  import { onMount } from 'svelte';
  export let WrappedComponent;
  export let endpoint;

  let data = null;
  let loading = true;

  onMount(async () => {
    const res = await fetch(endpoint);
    data = await res.json();
    loading = false;
  });
</script>

{#if loading}
  <p>Загрузка данных...</p>
{:else}
  <svelte:component this={WrappedComponent} {data} />
{/if}

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


Отличия HOC в Svelte от React

  • Слоты vs. children: В Svelte предпочтительнее использовать слоты для передачи контента, тогда как в React применяется props.children.
  • Реактивность: Прямое связывание пропсов и реактивных переменных упрощает HOC, сокращая boilerplate.
  • Прямой рендеринг: Svelte не требует return внутри функции HOC — используется <svelte:component>.

Советы по построению HOC в SvelteKit

  • Минимизировать вложенность HOC, чтобы избежать сложной структуры компонентов.
  • Использовать export let для передачи всех необходимых пропсов.
  • Сохранять исходный компонент чистым, избегая side-effects внутри него.
  • Встраивать слоты для гибкости и повторного использования контента.
  • Объединять HOC с реактивными сторями для управления глобальным состоянием.

Комбинирование нескольких HOC

HOC можно цепочечно применять для одного компонента:

<WithLogger WrappedComponent={WithCounter WrappedComponent={Button}} props={{ text: 'Click me' }} />

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


HOC и SvelteKit UI библиотеки

Многие Svelte UI библиотеки (например, Svelte Material UI, Shadcn UI) предоставляют базовые компоненты, которые легко расширять через HOC:

  • Добавление адаптивных стилей.
  • Автоматическое подключение темизации.
  • Повторное внедрение логики обработки событий.

Использование HOC в сочетании с UI библиотеками упрощает создание унифицированного интерфейса с минимальной дубликацией кода.