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

Управление состоянием в библиотеке Driver.js играет ключевую роль при построении сложных пользовательских сценариев: интерактивных туров, обучающих подсказок и пошаговых инструкций. Под состоянием понимается совокупность текущих параметров, описывающих положение тура: активный шаг, статус (запущен или завершён), конфигурация элементов и поведение интерфейса.

Driver.js не использует централизованное хранилище состояния (как Redux или Vuex), однако предоставляет механизмы для отслеживания и управления состоянием через API экземпляра.


Экземпляр драйвера как источник состояния

Создание экземпляра:

import Driver from "driver.js";

const driver = new Driver();

Экземпляр driver фактически инкапсулирует текущее состояние:

  • текущий активный шаг
  • список шагов
  • параметры конфигурации
  • статус выполнения тура

Текущее состояние шага

Driver.js позволяет получать информацию о текущем шаге:

const activeElement = driver.getActiveElement();
const activeStep = driver.getActiveStep();

Ключевые особенности:

  • getActiveElement() возвращает DOM-элемент текущего шага
  • getActiveStep() возвращает объект конфигурации шага

Пример структуры шага:

{
  element: "#button",
  popover: {
    title: "Кнопка",
    description: "Описание кнопки"
  }
}

Это позволяет:

  • синхронизировать UI с текущим шагом
  • изменять поведение в зависимости от шага
  • логировать прогресс пользователя

Управление жизненным циклом

Driver.js предоставляет методы для контроля состояния тура:

driver.start();
driver.moveNext();
driver.movePrevious();
driver.reset();
driver.hasNextStep();
driver.hasPreviousStep();

Состояния:

Состояние Описание
Не запущен До вызова start()
Активен Во время прохождения шагов
Завершён После последнего шага или reset

Проверка состояния

Проверка активности:

driver.isActivated();

Возвращает true, если тур активен.

Проверка наличия следующего шага:

if (driver.hasNextStep()) {
  driver.moveNext();
}

Это важно для предотвращения ошибок при навигации.


Синхронизация состояния с приложением

В сложных приложениях состояние Driver.js часто синхронизируется с внешним состоянием (например, React/Vue):

driver.defineSteps(steps);

driver.start();

setCurrentStep(driver.getActiveStep());

Обновление состояния при переходах:

driver.on("next", () => {
  updateUI(driver.getActiveStep());
});

События как инструмент управления состоянием

Driver.js предоставляет хуки, позволяющие отслеживать изменения:

const driver = new Driver({
  onHighlightStarted: (element) => {
    console.log("Шаг начался", element);
  },
  onHighlighted: (element) => {
    console.log("Шаг отображён");
  },
  onDeselected: (element) => {
    console.log("Шаг завершён");
  },
  onReset: () => {
    console.log("Тур сброшен");
  }
});

Назначение событий:

  • фиксация переходов между шагами
  • управление сторонними эффектами
  • обновление состояния приложения

Хранение состояния между сессиями

Driver.js не сохраняет состояние автоматически. Для этого используется внешнее хранилище:

LocalStorage

driver.on("next", () => {
  localStorage.setItem("tour-step", driver.getActiveStepIndex());
});

Восстановление состояния:

const savedStep = localStorage.getItem("tour-step");

if (savedStep !== null) {
  driver.start(savedStep);
}

Управление индексом шага

Driver.js использует индекс шагов для определения текущего состояния:

driver.getActiveStepIndex();

Можно запускать тур с конкретного шага:

driver.start(2);

Это полезно для:

  • возврата пользователя к месту остановки
  • запуска тура с определённого этапа

Динамическое изменение состояния

Driver.js позволяет изменять шаги во время выполнения:

driver.defineSteps([
  {
    element: "#new-element",
    popover: {
      title: "Новый шаг"
    }
  }
]);

Важно учитывать:

  • переопределение шагов сбрасывает текущее состояние
  • желательно выполнять изменения до start()

Условное управление шагами

Часто требуется пропуск шагов:

if (user.isAdmin) {
  driver.moveNext();
} else {
  driver.moveNext();
  driver.moveNext();
}

Более гибкий подход — формирование шагов заранее:

const steps = [];

if (user.isAdmin) {
  steps.push(adminStep);
}

driver.defineSteps(steps);

Управление состоянием UI

Driver.js влияет на DOM:

  • добавляет overlay
  • блокирует взаимодействие
  • подсвечивает элементы

Контроль через параметры:

const driver = new Driver({
  allowClose: false,
  overlayOpacity: 0.7,
  stagePadding: 5
});

Состояние интерфейса напрямую связано с состоянием тура.


Сброс состояния

Полный сброс:

driver.reset();

Это:

  • удаляет overlay
  • очищает активный шаг
  • завершает тур

После reset требуется новый start().


Частые ошибки управления состоянием

1. Попытка перехода без проверки:

driver.moveNext(); // ошибка, если шагов больше нет

Решение:

if (driver.hasNextStep()) {
  driver.moveNext();
}

2. Потеря состояния при перерисовке UI

В SPA-приложениях DOM может пересоздаваться:

  • текущий элемент исчезает
  • Driver.js теряет ссылку

Решение:

  • повторная инициализация
  • ожидание рендера

3. Несинхронизированные шаги

Если элементы появляются динамически:

setTimeout(() => {
  driver.start();
}, 500);

Лучше использовать события рендера.


Архитектурные подходы

1. Централизованное управление

Создание сервиса:

class TourManager {
  constructor() {
    this.driver = new Driver();
  }

  startTour(steps) {
    this.driver.defineSteps(steps);
    this.driver.start();
  }
}

2. Интеграция с состоянием приложения

React пример:

useEffect(() => {
  if (isTourActive) {
    driver.start();
  } else {
    driver.reset();
  }
}, [isTourActive]);

3. Декларативное описание шагов

Хранение конфигурации отдельно:

const tourSteps = [
  { element: "#a", popover: { title: "A" } },
  { element: "#b", popover: { title: "B" } }
];

Рекомендации по управлению состоянием

  • избегать изменения шагов во время выполнения
  • синхронизировать состояние с UI
  • использовать события вместо ручных проверок
  • сохранять прогресс при длинных турах
  • учитывать асинхронную загрузку DOM

Расширенное управление поведением

Комбинирование состояния и логики:

driver.on("next", () => {
  const stepIndex = driver.getActiveStepIndex();

  if (stepIndex === 2) {
    openModal();
  }
});

Работа с асинхронным состоянием

Если элемент появляется позже:

async function waitForElement(selector) {
  while (!document.querySelector(selector)) {
    await new Promise(r => setTimeout(r, 100));
  }
}

await waitForElement("#dynamic");

driver.start();

Контроль завершения

Отслеживание окончания:

driver.on("reset", () => {
  console.log("Тур завершён");
});

Или вручную:

if (!driver.hasNextStep()) {
  driver.reset();
}

Управление несколькими турами

Для разных сценариев:

const onboardingDri ver = new Driver();
const featureDriver = new Driver();

Каждый экземпляр имеет своё состояние.


Взаимодействие с пользовательскими действиями

Прерывание тура:

document.querySelector("#cancel").addEventListener("click", () => {
  driver.reset();
});

Переход по действию:

button.addEventListener("click", () => {
  driver.moveNext();
});

Глубокая настройка состояния

Driver.js позволяет управлять не только шагами, но и поведением:

const driver = new Driver({
  animate: true,
  opacity: 0.75,
  padding: 10
});

Эти параметры влияют на визуальное состояние интерфейса.


Вывод состояния в отладке

Полезно логировать:

console.log({
  activeStep: driver.getActiveStep(),
  index: driver.getActiveStepIndex(),
  active: driver.isActivated()
});

Это помогает при разработке сложных туров.


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