Паттерн observable store

Observable store — это паттерн управления состоянием, который позволяет создавать реактивные данные в компонентах FAST Element. Основная цель паттерна — отделение логики состояния от представления и обеспечение автоматического обновления DOM при изменении данных.


Основы реактивности в FAST Element

FAST Element использует реактивные свойства и шаблоны для синхронизации данных с DOM. Любое свойство класса компонента, помеченное как реактивное с помощью декоратора @observable, автоматически отслеживается:

import { FASTElement, observable } from "@microsoft/fast-element";

class MyComponent extends FASTElement {
  @observable count = 0;
}

При изменении count FAST Element инициирует повторный рендер связанной части шаблона. Это делает возможным построение observable store как отдельного объекта, который может использоваться несколькими компонентами одновременно.


Создание Observable Store

Store представляет собой объект с реактивными свойствами. В FAST Element можно реализовать store через обычный класс с декоратором @observable:

import { observable } from "@microsoft/fast-element";

export class CounterStore {
  @observable count = 0;

  increment() {
    this.count += 1;
  }

  decrement() {
    this.count -= 1;
  }
}

export const counterStore = new CounterStore();

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

  • @observable автоматически делает свойство реактивным.
  • Методы класса позволяют централизованно изменять состояние.
  • Store может быть экспортирован как singleton для совместного использования в нескольких компонентах.

Подключение Store к компоненту

Чтобы компонент использовал store, нужно добавить его как свойство и подписаться на обновления:

import { FASTElement, html, css, observable } from "@microsoft/fast-element";
import { counterStore } from "./counter-store.js";

const template = html<MyCounter>`
  <div>
    <p>Count: ${x => x.store.count}</p>
    <button @click=${x => x.store.increment()}>Increment</button>
    <button @click=${x => x.store.decrement()}>Decrement</button>
  </div>
`;

const styles = css`
  div { font-family: sans-serif; }
  button { margin: 5px; }
`;

export class MyCounter extends FASTElement {
  @observable store = counterStore;
}

MyCounter.define({ name: "my-counter", template, styles });

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

  • Свойство store реактивно.
  • Любые изменения store.count автоматически обновляют шаблон.
  • Один и тот же store можно использовать в нескольких компонентах для синхронизации состояния.

Реактивные вычисляемые свойства

FAST Element поддерживает computed properties через геттеры, которые зависят от реактивных данных store:

export class CounterStore {
  @observable count = 0;

  get isEven() {
    return this.count % 2 === 0;
  }

  increment() {
    this.count += 1;
  }
}

В шаблоне:

<p>Even: ${x => x.store.isEven}</p>

FAST Element автоматически пересчитает isEven при изменении count.


Подписка на изменения без шаблона

Иногда требуется реагировать на изменения состояния вне шаблона. Для этого используется метод subscribe у Observable:

counterStore.$count.subscribe(newValue => {
  console.log("Count changed to:", newValue);
});
  • $count — это автоматически созданный объект наблюдения для свойства count.
  • Подписка позволяет выполнять побочные эффекты при изменении состояния, например, логирование или асинхронные действия.

Обновление нескольких свойств одновременно

FAST Element позволяет группировать обновления с помощью Observable.track для оптимизации перерисовки:

import { Observable } from "@microsoft/fast-element";

Observable.track(() => {
  counterStore.count += 1;
  counterStore.otherValue = "new value";
});

Все изменения будут объединены, и шаблон обновится один раз вместо нескольких рендеров.


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

Observable store можно комбинировать с внешними хранилищами, такими как Redux или MobX. FAST Element предоставляет гибкий API, который позволяет использовать store как локальное или глобальное состояние без потери реактивности.


Практические рекомендации

  • Разделять логику состояния и представление: store отвечает только за данные и методы, компонент — за шаблон и взаимодействие.
  • Использовать singleton store для синхронизации данных между несколькими компонентами.
  • Для сложных состояний использовать вложенные observable объекты.
  • Избегать прямого изменения DOM через store — изменения должны проходить через реактивные свойства компонента.

Observable store в FAST Element обеспечивает простую, реактивную и централизованную модель управления состоянием, что упрощает создание масштабируемых и поддерживаемых приложений.