useContext для передачи данных

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


Создание и предоставление контекста

Для создания контекста используется функция createContext из Atomico:

import { createContext, useContext, useState, html } from "atomico";

const ThemeContext = createContext();

createContext возвращает объект с двумя ключевыми свойствами: Provider и Consumer. В Atomico чаще используется Provider в сочетании с хуком useContext.

Пример использования Provider:

function App() {
    const [theme, setTheme] = useState("light");

    return html`
        <${ThemeContext.Provider} value=${{ theme, setTheme }}>
            <${Toolbar} />
        <//>
    `;
}

Здесь value задаёт данные, доступные для всех дочерних компонентов. Любой компонент внутри <ThemeContext.Provider> сможет получить доступ к theme и setTheme.


Получение данных с помощью useContext

Дочерние компоненты используют useContext для доступа к данным:

function Toolbar() {
    const { theme, setTheme } = useContext(ThemeContext);

    return html`
        <div>
            <p>Текущая тема: ${theme}</p>
            <button onCl ick=${() => setTheme(theme === "light" ? "dark" : "light")}>
                Переключить тему
            </button>
        </div>
    `;
}

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

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

Динамическое обновление контекста

Контекст поддерживает динамическое изменение значений. При обновлении состояния в родительском компоненте все дочерние компоненты, использующие useContext, обновятся:

function App() {
    const [theme, setTheme] = useState("light");

    const toggleTheme = () => setTheme(theme === "light" ? "dark" : "light");

    return html`
        <${ThemeContext.Provider} value=${{ theme, toggleTheme }}>
            <${Toolbar} />
            <${Content} />
        <//>
    `;
}

function Content() {
    const { theme } = useContext(ThemeContext);
    return html`<div style=${`background: ${theme === "light" ? "#fff" : "#333"}; color: ${theme === "light" ? "#000" : "#fff"}`}>
        Контент с темой ${theme}
    </div>`;
}

Любая функция, переданная через value, может использоваться для управления состоянием контекста, что делает useContext гибким инструментом для глобального состояния без сторонних библиотек.


Контекст с несколькими значениями

Atomico позволяет использовать несколько контекстов одновременно. Для этого создаются несколько createContext и используются соответствующие Provider:

const UserContext = createContext();
const SettingsContext = createContext();

function App() {
    const [user, setUser] = useState({ name: "Alex" });
    const [settings, setSettings] = useState({ notifications: true });

    return html`
        <${UserContext.Provider} value=${{ user, setUser }}>
            <${SettingsContext.Provider} value=${{ settings, setSettings }}>
                <${Dashboard} />
            <//>
        <//>
    `;
}

Компоненты внутри Dashboard могут подключаться к каждому контексту независимо:

function Dashboard() {
    const { user } = useContext(UserContext);
    const { settings, setSettings } = useContext(SettingsContext);

    return html`
        <div>
            <p>Пользователь: ${user.name}</p>
            <button onCl ick=${() => setSettings({ ...settings, notifications: !settings.notifications })}>
                Переключить уведомления
            </button>
        </div>
    `;
}

Контекст и производительность

Использование контекста может вызвать лишние перерисовки, если объект value создаётся заново при каждом рендере. Рекомендуется мемоизировать значения с помощью useMemo:

import { useMemo } from "atomico";

function App() {
    const [theme, setTheme] = useState("light");

    const contextValue = useMemo(() => ({ theme, setTheme }), [theme]);

    return html`
        <${ThemeContext.Provider} value=${contextValue}>
            <${Toolbar} />
        <//>
    `;
}

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


Передача функций и обработчиков через контекст

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

const ModalContext = createContext();

function App() {
    const [isOpen, setIsOpen] = useState(false);

    const openModal = () => setIsOpen(true);
    const closeModal = () => setIsOpen(false);

    return html`
        <${ModalContext.Provider} value=${{ isOpen, openModal, closeModal }}>
            <${Header} />
            <${Main} />
            <${Modal} />
        <//>
    `;
}

Дочерние компоненты могут использовать openModal и closeModal без прямого проброса через пропсы.


Итоговые принципы использования useContext

  • Контекст удобен для передачи глобальных данных между компонентами.
  • Provider задаёт доступные данные, а useContext позволяет их использовать.
  • Динамическое обновление контекста автоматически обновляет компоненты.
  • Для нескольких контекстов создаются независимые createContext и Provider.
  • Меморизация value помогает избежать лишних перерисовок.
  • Через контекст можно передавать не только состояние, но и функции, обработчики и объекты.

Эффективное использование useContext в Atomico позволяет создавать чистую архитектуру компонентов, минимизировать вложенность пропсов и централизовать управление состоянием приложения.