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

Mithril — минималистичный фреймворк для создания одностраничных приложений на JavaScript. Одной из ключевых задач при разработке крупных приложений является управление состоянием, особенно если оно используется в разных компонентах. В Mithril подход к глобальному состоянию отличается простотой и гибкостью, что позволяет избежать громоздких решений вроде Redux, если приложение не требует сложной логики.

Центральный объект состояния

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

const store = {
    user: null,
    todos: [],
    isLoading: false
};

Здесь store хранит все данные, которые должны быть доступны глобально. Его структура может быть произвольной, но рекомендуется четко разделять данные по категориям для удобства поддержки.

Модификация состояния

Изменение состояния осуществляется напрямую через объекты store. Например:

function addTodo(text) {
    store.todos.push({ text, completed: false });
    m.redraw(); // перерисовка компонентов после изменения состояния
}

function setUser(user) {
    store.user = user;
    m.redraw();
}

Ключевой момент: Mithril не отслеживает автоматически изменения в объектах, поэтому после любых модификаций необходимо явно вызывать m.redraw(), чтобы обновить интерфейс.

Реактивность и наблюдение за изменениями

Для упрощения управления состоянием можно использовать паттерн подписки на изменения. Например, создать функцию-обертку для наблюдаемого состояния:

function observable(initialValue) {
    let value = initialValue;
    const subscribers = [];

    return {
        get: () => value,
        set: (newValue) => {
            value = newValue;
            subscribers.forEach(fn => fn(value));
            m.redraw();
        },
        subscribe: (fn) => subscribers.push(fn)
    };
}

const count = observable(0);

count.subscribe((val) => console.log("New value:", val));

count.set(1); // в консоли: New value: 1

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

Интеграция с компонентами

Компоненты Mithril получают доступ к глобальному состоянию напрямую или через функции-обертки. Пример использования глобального хранилища в компоненте:

const TodoList = {
    view: () => m("ul", store.todos.map(todo =>
        m("li", { class: todo.completed ? "done" : "" }, todo.text)
    ))
};

const App = {
    view: () => m("div", [
        m("h1", "Todo App"),
        m(TodoList)
    ])
};

m.mount(document.body, App);

Любое изменение store.todos и вызов m.redraw() приведет к автоматической перерисовке списка задач.

Асинхронные операции и глобальное состояние

Для асинхронных запросов глобальное состояние может содержать флаги загрузки и ошибки:

function fetchTodos() {
    store.isLoading = true;
    m.redraw();
    fetch("/api/todos")
        .then(res => res.json())
        .then(data => {
            store.todos = data;
            store.isLoading = false;
            m.redraw();
        })
        .catch(() => {
            store.isLoading = false;
            m.redraw();
        });
}

Компоненты могут отображать индикатор загрузки на основе store.isLoading:

const Loader = {
    view: () => store.isLoading ? m("p", "Загрузка...") : null
};

Разделение состояния по модулям

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

const userStore = {
    user: null,
    setUser(user) {
        this.user = user;
        m.redraw();
    }
};

const todoStore = {
    todos: [],
    addTodo(todo) {
        this.todos.push(todo);
        m.redraw();
    }
};

Это облегчает поддержку, тестирование и масштабирование приложения, сохраняя при этом принцип единого источника правды.

Заключение по использованию

Глобальное состояние в Mithril строится вокруг простых объектов и функций. Основные правила:

  • Использовать единый объект или модульные объекты для хранения данных.
  • После изменения состояния всегда вызывать m.redraw().
  • Для реактивности можно применять паттерн observable.
  • Разделять состояние на модули для крупных приложений.
  • Асинхронные операции должны корректно управлять флагами загрузки и ошибки.

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