События компонентов через колбэки

Чтобы ребёнок сообщил родителю о событии, в Svelte 5 передают пропс-колбэк — обычную функцию.

«Событие — это когда ребёнок поднимает руку и кричит наверх: эй, родитель, кое-что случилось!»

Пропсы передают данные вниз. А как передать сигнал вверх — от ребёнка к родителю? Например, дочерняя кнопка нажата, и родитель должен это узнать. В Svelte 4 для этого был createEventDispatcher, но Svelte 5 упростил подход: события — это просто пропсы-колбэки. Родитель передаёт функцию вниз, ребёнок вызывает её, когда нужно.

Это та же модель, что и обычные DOM-обработчики, просто на уровне компонентов. И она прекрасно ложится в общую картину: всё — пропсы. Данные текут вниз как значения, события текут вверх как вызовы переданных функций.

<!-- LikeButton.svelte -->
<script>
  let { onLike } = $props();  // колбэк от родителя
  let count = $state(0);

  function handle() {
    count++;
    onLike?.(count);  // сообщаем наверх новое значение
  }
</script>

<button onclick={handle}>Лайк ({count})</button>

Родитель передаёт функцию через пропс с тем же именем:

<!-- родитель -->
<script>
  import LikeButton from './LikeButton.svelte';
  let likes = $state(0);
</script>

<LikeButton onLike={(n) => likes = n} />
<p>Всего лайков: {likes}</p>

Когда пользователь нажимает кнопку внутри LikeButton, ребёнок вызывает onLike(count), и родитель обновляет своё состояние. Оператор ?. защищает на случай, если колбэк не передали. Заметьте, насколько это прозрачнее старого диспетчера событий — нет специального API, просто функции.

Как это работает под капотом

Колбэк-события — это паттерн «обратного вызова». Родитель владеет функцией, ребёнок её вызывает. Смоделируем на чистом JS.

// События как колбэки: ребёнок вызывает функцию родителя
function LikeButton({ onLike }) {
  let count = 0;
  return {
    click() { count++; if (onLike) onLike(count); } // 'кричим наверх'
  };
}

let likes = 0;
const child = LikeButton({ onLike: (n) => { likes = n; console.log('родитель узнал:', likes); } });

child.click(); // родитель узнал: 1
child.click(); // родитель узнал: 2

Попробуй сам ▶ — вставь код в консоль браузера (F12 → Console) и нажми Enter, чтобы увидеть вывод.

  Родитель                       Ребёнок (LikeButton)
  --------                       --------------------
  onLike = (n) => likes = n  -->  получает как пропс
        ^                              |
        |  вызов onLike(count)         | клик по кнопке
        +------------------------------+

Частые ошибки

  • Искать createEventDispatcher в Svelte 5. Современный путь — колбэк-пропсы.
  • Вызывать колбэк без проверки. Используйте onLike?.(), если пропс необязательный.
  • Пытаться мутировать пропс вместо вызова колбэка. Поток вверх — только через функции.

Best practices

  • Называйте колбэки в стиле обработчиков: onLike, onSubmit, onClose.
  • Передавайте наверх данные, а не реализацию: ребёнок сообщает «что случилось», родитель решает «что делать».
  • Делайте колбэки необязательными там, где это уместно, и защищайте вызов ?..

Кто принимает решение

За паттерном колбэк-событий стоит важный принцип проектирования: ребёнок сообщает, что произошло, а решение, что с этим делать, принимает родитель. Кнопка лайка не знает, нужно ли отправить запрос на сервер, обновить счётчик в шапке или показать анимацию — она лишь кричит наверх «меня нажали». Это держит компоненты слабо связанными и переиспользуемыми: одну и ту же кнопку можно вставить в десяток мест, и в каждом родитель навесит свою реакцию. Старайтесь передавать наверх минимальные осмысленные данные — что случилось и с какими параметрами, — а не готовые действия. Тогда дочерний компонент остаётся универсальным, а вся специфичная логика концентрируется в родителе, который владеет контекстом. Этот разделённый поток — данные вниз, события вверх — образует ясную, предсказуемую архитектуру, в которой легко проследить, откуда взялось любое изменение состояния.

Итог: в Svelte 5 события — это пропсы-функции. Ребёнок вызывает переданный колбэк, родитель реагирует. Никакого специального API — данные вниз, вызовы вверх.

Проверьте себя
1. Как в Svelte 5 ребёнок сообщает родителю о событии?
AЧерез createEventDispatcher
BЧерез прямое изменение пропса
CЧерез вызов пропса-колбэка, переданного родителем
DЧерез глобальную шину событий
2. Почему вызов колбэка часто пишут как onLike?.(count)?
AЧтобы вызвать колбэк дважды
BЧтобы безопасно вызвать его, только если он передан (необязательный пропс)
CЭто синтаксис объявления пропса
DЧтобы превратить функцию в строку