Action паттерны

Action в контексте SvelteKit — это паттерн, позволяющий обрабатывать события взаимодействия пользователя с компонентами на уровне серверной логики, сохраняя реактивность фронтенда и обеспечивая согласованное управление состоянием. Основная идея заключается в том, что действие (например, сабмит формы или клик кнопки) можно привязать к функции, которая будет выполняться на сервере или в компоненте, а результат возвращать обратно в интерфейс без полного перезагрузки страницы.


Основы синтаксиса Action

Action в SvelteKit реализуется через директиву use:. Общий вид подключения:

<script>
  import { formAction } from '$lib/actions';

  function handleSubmit(node) {
    return formAction(node, async (data) => {
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: JSON.stringify(data),
      });
      return await response.json();
    });
  }
</script>

<form use:handleSubmit>
  <input name="username" required />
  <button type="submit">Отправить</button>
</form>

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

  • node — DOM-элемент, к которому привязывается action.
  • data — объект, содержащий значения полей формы или параметры события.
  • Возврат значения — может быть любым: объект состояния, сообщение об ошибке или результат выполнения запроса.

Паттерн асинхронных Action

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

<script>
  import { writable } from 'svelte/store';

  const status = writable('idle');

  async function asyncAction(node) {
    node.addEventListener('click', async () => {
      status.set('loading');
      try {
        const res = await fetch('/api/data');
        const json = await res.json();
        status.set('success');
        console.log(json);
      } catch (err) {
        status.set('error');
        console.error(err);
      }
    });
  }
</script>

<button use:asyncAction>Загрузить данные</button>
<p>{$status}</p>

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

  • Используется writable для реактивного отображения состояния действия.
  • Любые ошибки перехватываются и могут обновлять UI без перезагрузки страницы.
  • Асинхронные Actions хорошо интегрируются с API и серверными эндпоинтами.

Паттерн декомпозиции Action

Для сложных интерфейсов с множественными событиями часто применяют декомпозицию Action. Каждое действие инкапсулируется в отдельную функцию, которая возвращает объект с обработчиками и состоянием:

<script>
  function createCounterAction() {
    let count = 0;

    return (node) => {
      const increment = () => {
        count += 1;
        node.textContent = `Счетчик: ${count}`;
      };

      node.addEventListener('click', increment);

      return {
        destroy() {
          node.removeEventListener('click', increment);
        }
      };
    };
  }
</script>

<button use:createCounterAction>Нажми меня</button>

Преимущества:

  • Локальное состояние управляется внутри Action, не засоряя глобальный scope.
  • Легко подключать и отключать обработчики через метод destroy.
  • Повышает читаемость и повторное использование кода.

Комбинация нескольких Actions

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

<script>
  function logAction(node) {
    const handler = () => console.log('Клик выполнен');
    node.addEventListener('click', handler);
    return { destroy: () => node.removeEventListener('click', handler) };
  }

  function alertAction(node) {
    const handler = () => alert('Действие сработало');
    node.addEventListener('click', handler);
    return { destroy: () => node.removeEventListener('click', handler) };
  }
</script>

<button use:logAction use:alertAction>Кликни меня</button>
  • Все Actions выполняются по мере подключения, без конфликтов.
  • Каждый Action инкапсулирует собственную логику и управление событиями.
  • Методы destroy позволяют корректно очищать ресурсы при размонтировании компонента.

Интеграция с SvelteKit Forms

Action-паттерны тесно связаны с формами, позволяя обрабатывать сабмит через серверные endpoints:

<script>
  import { fail } from '@sveltejs/kit';

  export const actions = {
    default: async ({ request }) => {
      const data = await request.formData();
      const username = data.get('username');

      if (!username) return fail(400, { missing: true });

      // Дальнейшая обработка, например запись в базу данных
      return { success: true, username };
    }
  };
</script>

<form method="post" use:enhance>
  <input name="username" required />
  <button type="submit">Отправить</button>
</form>
  • Action можно связывать напрямую с методами HTTP (POST, PUT, DELETE).
  • Ошибки обрабатываются через объект fail, возвращаемый SvelteKit.
  • Состояние формы остаётся реактивным благодаря паттерну enhance или кастомным Action.

Ключевые практики при работе с Actions

  1. Изоляция логики — каждый Action должен быть максимально автономным.
  2. Очистка ресурсов — обязательно реализовать destroy, если Action подписан на события.
  3. Асинхронность — использовать async/await для запросов, чтобы не блокировать интерфейс.
  4. Повторное использование — декомпозиция и композиция позволяют применять один Action в нескольких компонентах.
  5. Обработка ошибок — централизованное управление состоянием ошибок повышает стабильность UI.

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