Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Побочные эффекты: $effect

Побочный эффект — действие, которое выходит за пределы разметки компонента: сменить заголовок вкладки, записать данные в localStorage, запустить таймер. Для таких действий есть руна $effect: она выполняет функцию после появления компонента на странице и повторяет её, когда меняется прочитанное в ней состояние.

Первый эффект

<!-- src/routes/+page.svelte -->
<script>
  let todos = $state([{ id: 1, text: 'Купить хлеб', done: false }]);
 
  $effect(() => {
    document.title = `Задач: ${todos.length}`;
  });
</script>
 
<button onclick={() => todos.push({ id: Date.now(), text: 'Новая', done: false })}>
  Добавить
</button>

Вкладка браузера получит заголовок «Задач: 1», а после каждого клика число в нём вырастет. Зависимости не нужно перечислять: Svelte сам видит, что функция читает todos.length.

Сохранение в localStorage

Эффект удобен, чтобы сохранять список задач между перезагрузками:

<!-- src/routes/+page.svelte -->
<script>
  let todos = $state([]);
 
  // 1. Один раз читаем сохранённый список
  $effect(() => {
    todos = JSON.parse(localStorage.getItem('todos') ?? '[]');
  });
 
  // 2. Сохраняем список при каждом изменении
  $effect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  });
</script>
 
<p>Задач: {todos.length}</p>
<button onclick={() => todos.push({ id: Date.now(), text: 'Новая', done: false })}>
  Добавить
</button>

Добавьте пару задач и обновите страницу — число сохранится. Эффекты выполняются по порядку и только в браузере, поэтому к localStorage внутри них обращаться безопасно. А вот код в начале <script> SvelteKit выполняет и на сервере, где localStorage нет, — поэтому список читается не в $state(...), а в эффекте. Первый эффект только записывает todos и ничего не читает из состояния, поэтому больше не перезапускается.

Заметьте: JSON.stringify обходит весь массив, поэтому эффект перезапускается при любом изменении внутри него.

Очистка

Если эффект запускает что-то долгоживущее — таймер, подписку, — верните из него функцию очистки. Svelte вызовет её перед повторным запуском эффекта и при удалении компонента:

<script>
  let seconds = $state(0);
 
  $effect(() => {
    const timer = setInterval(() => seconds++, 1000);
    return () => clearInterval(timer);
  });
</script>
 
<p>Вы на странице {seconds} с</p>

Число на странице растёт каждую секунду. Когда компонент исчезнет, таймер остановится и не будет работать впустую.

Когда эффект не нужен

Для значений, вычисляемых из состояния, используйте $derived, а для реакции на клик — обработчик события. $effect нужен только для связи с внешним миром: DOM, хранилища браузера, сторонние библиотеки.

Частая ошибка

Менять в эффекте то же состояние, которое он читает:

<script>
  let count = $state(0);
 
  $effect(() => {
    count = count + 1; // эффект перезапускает сам себя
  });
</script>

Эффект изменил count, это вызвало его повторный запуск, и так без конца — Svelte остановит цикл с ошибкой. Если нужно новое значение из старого, это задача для $derived или обработчика события.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.