Provider компоненты

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


Создание Provider компонента

Для создания Provider компонента используется функция provide. Она позволяет объявить контекст и передавать его значения потомкам:

import { c, provide } from "atomico";

const ThemeContext = Symbol("ThemeContext");

function ThemeProvider({ theme, children }) {
    provide(ThemeContext, theme);
    return <host>{children}</host>;
}

ThemeProvider.props = {
    theme: String
};

export { ThemeProvider, ThemeContext };

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

  • provide(context, value) регистрирует значение контекста для всех потомков.
  • context обычно создаётся через Symbol, чтобы исключить коллизии имён.
  • Provider не ограничен конкретной структурой данных; можно передавать объекты, функции, строки, числа и массивы.

Использование Provider и Context в дочерних компонентах

Для получения значения контекста в дочернем компоненте применяется функция useContext:

import { c, useContext } from "atomico";
import { ThemeContext } from "./ThemeProvider";

function ThemedButton() {
    const theme = useContext(ThemeContext);

    return (
        <host style={{ background: theme.background, color: theme.color }}>
            Click me
        </host>
    );
}

export default c(ThemedButton);

Особенности работы:

  • Если значение контекста не было предоставлено родителем, useContext вернёт undefined.
  • useContext можно использовать в любом компоненте, находящемся в дереве под Provider.
  • Компоненты автоматически подписываются на изменения контекста. При изменении значения Provider дочерние компоненты перерисовываются.

Комбинирование нескольких Provider компонентов

Atomico позволяет создавать несколько независимых контекстов. Это удобно для изоляции различных типов данных:

const UserContext = Symbol("UserContext");
const ThemeContext = Symbol("ThemeContext");

function App() {
    return (
        <ThemeProvider theme={{ background: "#000", color: "#fff" }}>
            <UserProvider user={{ name: "Alice" }}>
                <ThemedButton />
                <UserProfile />
            </UserProvider>
        </ThemeProvider>
    );
}
  • Каждый Provider управляет своим контекстом независимо.
  • Дочерние компоненты могут подписываться только на нужный контекст.

Передача функций через Provider

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

function CounterProvider({ children }) {
    const [count, setCount] = useState(0);

    provide(CounterContext, {
        count,
        increment: () => setCount(count + 1),
        decrement: () => setCount(count - 1)
    });

    return <host>{children}</host>;
}
  • Дочерние компоненты могут вызывать методы контекста напрямую.
  • Подход полностью совместим с реактивной моделью Atomico: при вызове setCount все компоненты, использующие CounterContext, перерисуются.

Особенности реактивности

Atomico обеспечивает автоматическое обновление компонентов при изменении контекста, если передаваемое значение является реактивным:

  • Простые типы (String, Number, Boolean) обновляются при полной замене значения.
  • Для объектов и массивов рекомендуется использовать иммутабельные подходы, чтобы изменения корректно отслеживались.
  • Provider не хранит внутреннего состояния сам по себе, он лишь транслирует данные вниз по дереву.

Практические советы

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

Интеграция с хуками Atomico

Provider компоненты идеально сочетаются с другими хуками Atomico:

  • useState и useReducer для управления состоянием внутри Provider.
  • useEffect для синхронизации состояния с внешними источниками.
  • useContext для подписки на данные и реактивного обновления компонентов.

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