Паттерны управления глобальным состоянием

Управление состоянием — ключевой аспект при разработке сложных приложений. В Preact, как и в других фронтенд-фреймворках, различают локальное и глобальное состояние. Локальное состояние компонента ограничено его жизненным циклом, тогда как глобальное состояние позволяет синхронизировать данные между разными компонентами без передачи пропсов через множество уровней.

Контекст (Context API)

Context API в Preact предоставляет встроенный механизм управления глобальным состоянием без необходимости сторонних библиотек. Контекст создаётся через createContext:

import { createContext } from 'preact';
import { useState, useContext } from 'preact/hooks';

const AppContext = createContext();

function AppProvider({ children }) {
    const [user, setUser] = useState(null);
    return (
        <AppContext.Provider value={{ user, setUser }}>
            {children}
        </AppContext.Provider>
    );
}

function UserProfile() {
    const { user } = useContext(AppContext);
    return <div>{user ? `Привет, ${user.name}` : 'Гость'}</div>;
}

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

  • Provider оборачивает часть дерева компонентов, предоставляя доступ к состоянию.
  • useContext позволяет любому компоненту потреблять состояние без передачи пропсов.
  • Контекст удобен для данных, которые редко меняются, но часто читаются (например, тема, язык, авторизация).

Сторонние библиотеки для глобального состояния

Preact совместим с большинством инструментов управления состоянием из экосистемы React, включая Redux, Zustand, MobX. Это даёт гибкость в выборе паттерна в зависимости от масштаба проекта.

Пример с Zustand:

import create from 'zustand';

const useStore = create(set => ({
    count: 0,
    increment: () => set(state => ({ count: state.count + 1 })),
    reset: () => set({ count: 0 }),
}));

function Counter() {
    const { count, increment, reset } = useStore();
    return (
        <div>
            <p>{count}</p>
            <button onCl ick={increment}>+</button>
            <button onCl ick={reset}>Сброс</button>
        </div>
    );
}

Преимущества:

  • Простая и лаконичная синтаксисизация состояния.
  • Минимальное количество обёрток и boilerplate.
  • Реактивное обновление компонентов без лишних ререндеров.

Паттерн Flux / Redux

Для крупных приложений, где требуется строгая структура потоков данных, используется Flux-подобный подход или Redux. В Preact можно использовать preact-redux для интеграции:

import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

const initialState = { todos: [] };

function reducer(state = initialState, action) {
    switch (action.type) {
        case 'ADD_TODO':
            return { ...state, todos: [...state.todos, action.payload] };
        default:
            return state;
    }
}

const store = createStore(reducer);

function TodoList() {
    const todos = useSelector(state => state.todos);
    const dispatch = useDispatch();

    const addTodo = text => dispatch({ type: 'ADD_TODO', payload: text });

    return (
        <div>
            <ul>{todos.map(todo => <li key={todo}>{todo}</li>)}</ul>
            <button onCl ick={() => addTodo('Новое задание')}>Добавить</button>
        </div>
    );
}

function App() {
    return (
        <Provider store={store}>
            <TodoList />
        </Provider>
    );
}

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

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

Локальные подписки и кастомные хуки

Для более тонкого контроля глобального состояния применяются кастомные хуки, которые инкапсулируют логику подписки на изменения:

import { useState, useEffect } from 'preact/hooks';

const subscribers = new Set();
let globalState = { theme: 'light' };

export function useGlobalTheme() {
    const [theme, setTheme] = useState(globalState.theme);

    useEffect(() => {
        const callback = () => setTheme(globalState.theme);
        subscribers.add(callback);
        return () => subscribers.delete(callback);
    }, []);

    const updateTheme = newTheme => {
        globalState.theme = newTheme;
        subscribers.forEach(cb => cb());
    };

    return [theme, updateTheme];
}

Преимущества:

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

Выбор паттерна

  • Маленькие проекты: Context API или локальные подписки.
  • Средние проекты: Zustand или другие легковесные библиотеки.
  • Крупные проекты: Redux или Flux для структурированного потока данных.

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