Декларативная подписка

Декларативная подписка — это механизм, позволяющий компонентам Atomico автоматически реагировать на изменения данных или внешних источников, минимизируя ручное управление подписками и их отпиской. Она строится на принципах реактивности и удобна для интеграции с асинхронными потоками событий.


Основы подписки

В Atomico декларативная подписка реализуется через функцию useSubscription. Она принимает источник данных и возвращает текущее состояние этого источника, автоматически обновляя компонент при изменении данных.

import { component, useSubscription } from "atomico";

function Clock() {
    const time = useSubscription(() => new Date(), 1000);

    return <host>{time.toLocaleTimeString()}</host>;
}

export default component(Clock);

В этом примере useSubscription получает функцию-источник, которая вызывается с интервалом в 1000 миллисекунд. Компонент Clock автоматически перерисовывается при каждом обновлении времени.

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

  • Источник может быть функцией, возвращающей данные напрямую.
  • Можно указывать интервал или другую стратегию обновления.
  • Подписка автоматически очищается при удалении компонента из DOM.

Подписка на асинхронные потоки

Atomico позволяет подключаться к промисам, событиям и любым объектам, реализующим интерфейс EventTarget.

import { component, useSubscription } from "atomico";

function MouseTracker() {
    const position = useSubscription((set) => {
        const handler = (e) => set({ x: e.clientX, y: e.clientY });
        window.addEventListener("mousemove", handler);
        return () => window.removeEventListener("mousemove", handler);
    }, { x: 0, y: 0 });

    return <host>
        X: {position.x}, Y: {position.y}
    </host>;
}

export default component(MouseTracker);

Здесь подписка создаёт слушатель события mousemove и автоматически отписывается при демонтировании компонента. Объект set используется для обновления состояния подписки.

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

  • Подписка возвращает функцию очистки ресурсов.
  • Начальное состояние задаётся вторым аргументом.
  • Подписка может использоваться для любых событийных или асинхронных источников.

Реактивная зависимость

Подписка может отслеживать изменения зависимостей, аналогично хук useEffect в React.

import { component, useSubscription, useProp } from "atomico";

function UserInfo() {
    const userId = useProp("userId");
    const user = useSubscription(
        async (set) => {
            const res = await fetch(`/api/user/${userId}`);
            set(await res.json());
        },
        null,
        [userId]
    );

    return <host>{user ? user.name : "Загрузка..."}</host>;
}

export default component(UserInfo);
  • Третий аргумент useSubscription — массив зависимостей.
  • Подписка пересоздаётся при изменении userId.
  • Асинхронные операции интегрируются без утечек памяти, так как функция очистки вызывается автоматически.

Композиция подписок

Atomico позволяет комбинировать несколько подписок внутри одного компонента. Каждая подписка управляет своим состоянием и не мешает другим.

import { component, useSubscription } from "atomico";

function Dashboard() {
    const time = useSubscription(() => new Date(), 1000);
    const windowSize = useSubscription((set) => {
        const handler = () => set({ width: window.innerWidth, height: window.innerHeight });
        window.addEventListener("resize", handler);
        return () => window.removeEventListener("resize", handler);
    }, { width: window.innerWidth, height: window.innerHeight });

    return <host>
        Время: {time.toLocaleTimeString()}<br/>
        Размер окна: {windowSize.width}x{windowSize.height}
    </host>;
}

export default component(Dashboard);

Выводы по композиции:

  • Каждая подписка полностью изолирована.
  • Можно создавать сложные интерфейсы с множеством источников данных.
  • Обновления подписок происходят независимо друг от друга.

Подписка с мемоизацией

Для оптимизации можно использовать мемоизацию источников. Это предотвращает лишние перерисовки при неизменных данных.

import { component, useSubscription, useMemo } from "atomico";

function Fibonacci({ n }) {
    const fib = useMemo(() => {
        const compute = (x) => (x <= 1 ? 1 : compute(x - 1) + compute(x - 2));
        return compute(n);
    }, [n]);

    const value = useSubscription(() => fib, [fib]);

    return <host>Fib({n}) = {value}</host>;
}

export default component(Fibonacci);
  • useMemo кэширует вычисление функции.
  • Подписка перерисовывает компонент только при изменении fib.

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

  • Использовать декларативные подписки для асинхронных данных, событий, таймеров и внешних потоков.
  • Обязательно возвращать функцию очистки для ресурсов, чтобы избегать утечек памяти.
  • Для сложных зависимостей применять массив зависимостей.
  • Комбинировать подписки безопасно, каждая остаётся независимой.
  • Мемоизация и кеширование данных помогает снизить нагрузку на перерисовки.

Декларативная подписка в Atomico обеспечивает чистый, реактивный и безопасный способ управления состоянием компонентов, особенно в динамических интерфейсах с множеством асинхронных источников.