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

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

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

Локальное состояние компонента

Каждый компонент Marko может содержать своё локальное состояние, которое хранится внутри него. Это состояние используется для хранения данных, с которыми работает компонент. Оно определяется в теле компонента и может быть изменено через механизмы, такие как события или методы компонента.

Инициализация состояния

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

class MyComponent {
  onCreate() {
    this.state = {
      count: 0
    };
  }
}

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

this.setState({ count: this.state.count + 1 });

Реактивность

Marko поддерживает реактивность, что позволяет обновлять отображение компонента в ответ на изменения состояния. Когда состояние компонента изменяется, Marko автоматически отслеживает зависимости и обновляет только те части интерфейса, которые зависят от изменённых данных.

<div>
  <span>Количество: ${state.count}</span>
  <button on-click="increment">Увеличить</button>
</div>
class MyComponent {
  increment() {
    this.setState({ count: this.state.count + 1 });
  }
}

Здесь кнопка вызывает метод increment, который обновляет состояние, а Marko автоматически обновляет число, отображаемое на странице.

Состояние страницы

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

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

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

// Серверная часть
const state = { user: 'John Doe' };
res.marko(template, { state });
<!-- Шаблон Marko -->
<div>
  Привет, ${state.user}!
</div>

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

Глобальное состояние

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

Использование глобального хранилища

Для управления глобальным состоянием в Marko можно использовать различные подходы. Одним из них является использование специальных библиотек для управления состоянием, таких как Redux или Flux, но более часто используют подходы, встроенные в сам фреймворк.

Marko позволяет передавать данные на глобальном уровне через контекст приложения или использовать глобальные события. Пример глобального состояния через контекст:

class App {
  onCreate() {
    this.globalState = { theme: 'dark' };
  }
}
<div class="app ${globalState.theme}">
  <!-- Контент страницы -->
</div>

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

Использование глобальных событий

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

// В одном компоненте
this.emit('updateTheme', 'light');
// В другом компоненте
this.on('updateTheme', (newTheme) => {
  this.setState({ theme: newTheme });
});

Управление состоянием в асинхронных приложениях

В современных веб-приложениях асинхронные операции (например, запросы к серверу или работа с веб-сокетами) становятся важной частью логики. Marko предоставляет способы работы с состоянием в контексте асинхронных задач.

Асинхронные данные

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

class MyComponent {
  async onCreate() {
    this.state = { data: null };
    const response = await fetch('/api/data');
    const data = await response.json();
    this.setState({ data });
  }
}

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

Использование асинхронных событий

Когда нужно обновить состояние на основе внешних событий, таких как веб-сокеты или таймеры, также используются асинхронные методы:

this.on('dataReceived', async (data) => {
  this.setState({ data });
});

В этом примере компонент реагирует на получение данных в асинхронном режиме.

Интеграция с внешними хранилищами состояния

В крупных приложениях может быть целесообразно интегрировать Marko с внешними хранилищами состояния, такими как Redux или MobX. Эти библиотеки предоставляют расширенные возможности для управления глобальным состоянием и могут быть использованы в Marko с помощью плагинов или адаптеров.

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

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

import { connect } from 'marko-redux';

class MyComponent {
  // методы компонента
}

export default connect(MyComponent, (state) => ({
  user: state.user
}));

В этом примере компонент подключается к хранилищу Redux и получает данные о пользователе из глобального состояния.

Заключение

Паттерны управления состоянием в Marko предоставляют мощные и гибкие инструменты для работы с данными в веб-приложениях. От локального состояния компонентов до интеграции с глобальными хранилищами — каждый из этих подходов позволяет создавать масштабируемые и поддерживаемые приложения.