Подписка на изменения

Фреймворк Fresh для JavaScript и Deno предоставляет современный подход к построению реактивных веб-приложений с минимальной зависимостью от клиентского JavaScript. Одной из ключевых концепций является подписка на изменения данных, которая позволяет автоматически обновлять интерфейс при изменении состояния без явного вмешательства разработчика.


Реактивные сигналы

В основе механизма подписки лежат сигналы (Signal). Сигналы представляют собой объекты, которые хранят текущее значение и уведомляют подписчиков при его изменении. Они используются для создания реактивного состояния компонентов.

Пример создания сигнала:

import { signal } from "@preact/signals";

const count = signal(0);
  • count.value — текущее значение сигнала.
  • Изменение через count.value = новое_значение автоматически уведомляет все подписанные компоненты.

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


Подписка на сигналы

Подписка на изменения сигнала осуществляется с помощью метода .subscribe():

const unsubscribe = count.subscribe((newValue) => {
  console.log("Новое значение count:", newValue);
});
  • subscribe принимает колбэк, который вызывается при каждом изменении значения сигнала.
  • Возвращаемая функция unsubscribe позволяет прекратить подписку, что важно для управления памятью в больших приложениях.

Подписка может быть использована для:

  • Обновления DOM напрямую.
  • Вызова побочных эффектов, таких как запросы к серверу.
  • Логирования изменений состояния.

Реактивные эффекты

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

import { effect } from "@preact/signals";

effect(() => {
  console.log("Текущее значение count:", count.value);
});
  • Все сигналы, доступ к которым происходит внутри effect, становятся зависимостями.
  • При изменении любого из сигналов вызывается колбэк эффекта.
  • Эффекты автоматически управляют подписками, упрощая очистку ресурсов.

Комбинирование сигналов

Сигналы можно комбинировать для создания производных значений:

import { computed } from "@preact/signals";

const doubleCount = computed(() => count.value * 2);

effect(() => {
  console.log("Двойное значение count:", doubleCount.value);
});
  • computed создает производное значение, которое пересчитывается только при изменении зависимых сигналов.
  • Эффективная модель предотвращает лишние вычисления и ререндеры компонентов.

Подписка в компонентах Fresh

В Fresh подписка на изменения данных интегрирована напрямую в JSX через сигналы:

export default function Counter() {
  const count = signal(0);

  return (
    <div>
      <p>Счётчик: {count}</p>
      <button onCl ick={() => count.value++}>Увеличить</button>
    </div>
  );
}
  • Обновление count.value автоматически перерисовывает текст внутри <p>.
  • Не требуется использование useState или других хуков, как в классическом React.
  • Компоненты остаются чистыми и декларативными, а синхронизация состояния с UI — прозрачной.

Управление жизненным циклом подписок

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

import { effect } from "@preact/signals";

effect((cleanup) => {
  const interval = setInterval(() => count.value++, 1000);
  
  cleanup(() => clearInterval(interval));
});
  • cleanup вызывается при размонтировании эффекта или компонента.
  • Позволяет безопасно управлять таймерами, подписками на WebSocket или события.

Интеграция с внешними источниками данных

Подписка на внешние источники данных, такие как WebSocket или API, реализуется через сигналы:

import { signal, effect } from "@preact/signals";

const messages = signal([]);

const ws = new WebSocket("wss://example.com/chat");

ws.onmess age = (event) => {
  messages.value = [...messages.value, event.data];
};

effect(() => {
  console.log("Сообщения:", messages.value);
});
  • Новые данные автоматически добавляются в сигнал.
  • Интерфейс реагирует на изменения без дополнительных ререндеров.
  • Можно комбинировать с computed для фильтрации или агрегации данных.

Практические рекомендации

  • Использовать сигналы для локального состояния компонентов.
  • Для производных значений применять computed.
  • Для побочных эффектов использовать effect, всегда учитывая очистку ресурсов.
  • Подписки на внешние источники данных хранить в сигналах и управлять их жизненным циклом через эффекты.
  • Минимизировать глубокие цепочки производных значений для оптимизации производительности.

Механизм подписки на изменения в Fresh обеспечивает простую, прозрачную и эффективную реактивность, позволяя строить динамические интерфейсы без лишней сложности и большого объема клиентского кода.