В Stimulus состояние — это не глобальное хранилище данных и не
абстрактный слой поверх приложения. Оно локально, привязано к DOM и
контроллерам, и существует ровно столько, сколько существует
соответствующий фрагмент разметки. Управление состоянием при переходах
напрямую связано с жизненным циклом контроллеров, механизмом
values, работой с событиями и сохранением данных между
изменениями DOM.
Под переходами понимаются:
Каждый контроллер Stimulus проходит через чётко определённые стадии:
initialize() — вызывается один раз при создании
экземпляра контроллера;connect() — вызывается каждый раз, когда контроллер
связывается с DOM;disconnect() — вызывается при удалении элемента из
DOM.Состояние, хранящееся в свойствах экземпляра контроллера,
уничтожается при disconnect. Это ключевой
момент при проектировании переходов: любые данные, которые должны
пережить удаление DOM-узла, не могут храниться только в памяти
контроллера.
export default class extends Controller {
connect() {
this.counter = 0
}
increment() {
this.counter++
}
}
При любом переходе, приводящем к пересозданию элемента,
counter будет сброшен.
Механизм values предоставляет декларативный способ
хранения состояния, привязанного к DOM через data-*
атрибуты. Такие значения автоматически восстанавливаются при повторном
подключении контроллера.
export default class extends Controller {
static values = {
step: Number
}
next() {
this.stepValue++
}
}
<div data-controller="wizard"
data-wizard-step-value="1">
</div>
Ключевые свойства values:
String, Number,
Boolean, Array, Object);Это основной инструмент сохранения состояния при переходах, не требующий внешних хранилищ.
При использовании Turbo страницы могут заменяться без полной перезагрузки, но DOM при этом всё равно уничтожается и создаётся заново. Контроллеры Stimulus пересоздаются, если не используется кэширование.
Особенности:
values сохраняются, если они присутствуют в
HTML.Для устойчивого состояния при переходах:
sessionStorage или localStorage.connect() {
const saved = sessionStorage.getItem("wizard-step")
if (saved) this.stepValue = Number(saved)
}
stepValueChanged(value) {
sessionStorage.setItem("wizard-step", value)
}
При использовании Turbo Frames или ручной подмены HTML через fetch возможны ситуации, когда часть DOM обновляется, а часть остаётся.
Варианты поведения:
Для устойчивости состояния:
initialize;connect;targetConnected
и targetDisconnected.static targets = ["item"]
itemTargetConnected(element) {
// восстановление состояния для нового элемента
}
Распространённая ошибка — хранение состояния исключительно в DOM-классах или текстовом содержимом. При переходах это приводит к рассинхронизации.
Рекомендуемая схема:
values или
внешнем хранилище;statusValueChanged(value) {
this.element.classList.toggle("is-active", value === "active")
}
Такой подход делает восстановление состояния детерминированным после любого перехода.
При сложных интерфейсах состояние может быть распределено между несколькими контроллерами. При переходах важно избегать скрытых зависимостей.
Подходы:
data-* атрибутов
родительского элемента;this.dispatch("change", { detail: { step: this.stepValue } })
Другие контроллеры могут подписываться на это событие, не завися от жизненного цикла инициатора.
При переходах с анимациями элементы часто временно удаляются или скрываются. В таких случаях:
disconnect может вызываться раньше, чем завершится
анимация;Практика:
disconnect() {
this.saveState()
}
Stimulus не предназначен для централизованного state management, но при грамотной работе с переходами позволяет надёжно управлять локальным состоянием без усложнения архитектуры.
values — основной инструмент для переживания
переходов;Такой подход обеспечивает стабильную работу интерфейса при любых переходах, не нарушая философию Stimulus и сохраняя простоту кода.