События приложения и глобальное состояние

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

Контроллеры в Stimulus регистрируются через data-controller, а методы, обрабатывающие события, — через data-action. Пример базовой привязки:

<div data-controller="example">
  <button data-action="click->example#greet">Сказать привет</button>
</div>
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  greet() {
    console.log("Привет!")
  }
}

Связь контроллеров и событий

События DOM в Stimulus могут быть локальными и глобальными. Локальные события обрабатываются внутри одного контроллера. Например, клик на кнопку вызывает метод контроллера, как показано выше.

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

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

// Отправка события
window.dispatchEvent(new CustomEvent("user:logged_in", {
  detail: { username: "ivan" }
}));

// Подписка в другом контроллере
export default class extends Controller {
  connect() {
    window.addEventListener("user:logged_in", this.handleLogin)
  }

  disconnect() {
    window.removeEventListener("user:logged_in", this.handleLogin)
  }

  handleLogin = (event) => {
    console.log("Пользователь вошёл:", event.detail.username)
  }
}

Использование CustomEvent позволяет отделить логику компонентов и избегать жёсткой зависимости между контроллерами.

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

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

Пример структуры простого store:

const store = {
  state: {
    user: null,
    theme: "light"
  },
  subscribers: [],

  subscribe(callback) {
    this.subscribers.push(callback)
  },

  setState(newState) {
    this.state = { ...this.state, ...newState }
    this.subscribers.forEach(callback => callback(this.state))
  }
}

export default store

Контроллеры используют store следующим образом:

import { Controller } from "@hotwired/stimulus"
import store from "./store"

export default class extends Controller {
  connect() {
    store.subscribe(this.updateView)
  }

  updateView = (state) => {
    this.element.textContent = state.user ? `Привет, ${state.user}` : "Гость"
  }

  loginUser() {
    store.setState({ user: "ivan" })
  }
}

События и жизненный цикл контроллера

Контроллеры имеют методы жизненного цикла:

  • connect() — вызывается при подключении контроллера к DOM.
  • disconnect() — вызывается при удалении контроллера с DOM.
  • initialize() — вызывается один раз при создании экземпляра контроллера.

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

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

export default class extends Controller {
  connect() {
    window.addEventListener("theme:changed", this.updateTheme)
  }

  disconnect() {
    window.removeEventListener("theme:changed", this.updateTheme)
  }

  updateTheme = (event) => {
    document.body.dataset.theme = event.detail.theme
  }
}

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

  1. Минимизировать прямые зависимости между контроллерами. Использовать глобальные события или централизованное хранилище.
  2. Отделять состояние и визуализацию. Контроллер управляет DOM, а store — данными.
  3. Всегда очищать подписки в disconnect. Это предотвращает утечки памяти.
  4. Использовать CustomEvent с детальной информацией. Это делает события самодокументированными и удобными для расширения.
  5. Планировать жизненный цикл контроллера. Инициализация, подключение и отключение должны быть предсказуемыми и атомарными.

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