Сохранение состояния формы

Работа с состоянием формы в интерфейсах, использующих кастомные элементы выбора, требует учета того, что стандартное поведение <select> часто заменяется программной логикой. В библиотеке Choices.js управление состоянием строится вокруг синхронизации внутреннего представления выбранных значений и внешнего хранилища данных.


Базовая модель состояния Choices.js

Внутреннее состояние компонента строится на основе массива выбранных значений, где каждый элемент может содержать:

  • значение (value)
  • отображаемый текст (label)
  • дополнительные поля (custom properties)
  • состояние выбора (selected / disabled)

Экземпляр Choices предоставляет методы для получения текущего состояния:

  • getValue() — возвращает выбранные элементы в виде объектов
  • getValue(true) — возвращает только значения без метаданных
  • setValue() — программная установка состояния
  • clearStore() — очистка текущих данных

Сохранение состояния формы базируется на сериализации результата getValue() и последующем восстановлении через setValue().


Сериализация состояния формы

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

Наиболее распространённые подходы:

Сохранение только значений

const selected = choices.getValue(true);
localStorage.setItem('form_state', JSON.stringify(selected));

Данный вариант применяется, когда источником данных является статический список опций.


Сохранение расширенных объектов

const selected = choices.getValue();
sessionStorage.setItem('form_state', JSON.stringify(selected));

Такой подход необходим при динамических списках, где важны дополнительные свойства (например, категории, идентификаторы, флаги активности).


Восстановление состояния после загрузки

После инициализации экземпляра происходит обратная операция десериализации и передачи данных в компонент.

const saved = JSON.parse(localStorage.getItem('form_state') || '[]');

choices.setValue(saved);

При восстановлении важно учитывать структуру данных:

  • если сохранены только значения — используется массив строк
  • если сохранены объекты — требуется совпадение структуры с исходными элементами

Сохранение состояния в событиях изменения

Оптимальная стратегия — привязка сохранения к событию изменения выбора:

element.addEventListener('change', () => {
  const state = choices.getValue();
  localStorage.setItem('form_state', JSON.stringify(state));
});

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

element.addEventListener('addItem', () => {
  persist();
});

element.addEventListener('removeItem', () => {
  persist();
});

Разделение sessionStorage и localStorage

Выбор механизма хранения определяется жизненным циклом данных:

sessionStorage

Используется при необходимости сохранения состояния только в рамках одной сессии браузера. После закрытия вкладки данные удаляются.

localStorage

Применяется для долгосрочного сохранения состояния формы между визитами пользователя.


Восстановление состояния при асинхронной загрузке данных

При использовании динамических источников данных порядок инициализации становится критичным. Частая проблема — попытка восстановить состояние до загрузки списка опций.

Корректная последовательность:

  1. загрузка данных (API, fetch)
  2. инициализация Choices.js
  3. восстановление состояния
fetch('/api/options')
  .then(r => r.json())
  .then(data => {
    const choices = new Choices(select, {
      choices: data
    });

    const saved = JSON.parse(localStorage.getItem('state') || '[]');
    choices.setValue(saved);
  });

Проблема несоответствия данных

Состояние может стать неконсистентным, если:

  • изменились value у опций
  • удалены ранее сохраненные элементы
  • изменена структура данных

Для обработки таких случаев используется фильтрация:

const validValues = saved.filter(item =>
  choices._store.choices.some(c => c.value === item.value)
);

Сохранение множественного выбора

Для multi-select сценариев состояние хранится как массив:

const values = choices.getValue(true);
localStorage.setItem('multi', JSON.stringify(values));

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

choices.setValue(JSON.parse(localStorage.getItem('multi')));

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


Интеграция с формами и submit-событием

При отправке формы состояние часто синхронизируется с скрытыми полями:

form.addEventListener('submit', () => {
  hiddenInput.value = JSON.stringify(choices.getValue(true));
});

Такой подход обеспечивает совместимость с классической серверной обработкой форм.


Очистка состояния

Сброс формы должен сопровождаться удалением сохраненного состояния:

choices.clearStore();
localStorage.removeItem('form_state');

Дополнительно может потребоваться сброс DOM-значений:

form.reset();

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

В одностраничных приложениях состояние часто синхронизируется с роутером или глобальным стором.

Подходы:

  • хранение в Redux/Vuex/Pinia
  • синхронизация через localStorage
  • реактивные подписки на события Choices.js

Пример промежуточного слоя:

function syncState() {
  store.dispatch('updateChoices', choices.getValue(true));
}

Дедупликация и оптимизация записи состояния

При частых изменениях (поиск, ввод, удаление) возникает избыточное количество операций записи в storage. Используется debounce:

const persist = debounce(() => {
  localStorage.setItem('state', JSON.stringify(choices.getValue()));
}, 300);

Это снижает нагрузку на браузер и предотвращает блокировки UI.


Хранение дополнительных метаданных

Choices позволяет расширять элементы через кастомные свойства. При сохранении состояния такие данные могут быть критичны:

{
  value: '1',
  label: 'Option',
  customProperties: {
    group: 'A',
    priority: 10
  }
}

Сериализация должна учитывать эти поля, иначе восстановление приведет к потере логики отображения.


Состояние disabled и hidden элементов

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

  • disabled-элементы могут сохраняться, но не восстанавливаться
  • hidden-элементы часто требуют отдельной обработки

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

if (option.disabled) continue;
choices.setChoiceByValue(option.value);

Контроль целостности состояния

Для сложных форм применяется контроль версий состояния:

const state = {
  version: 1,
  values: choices.getValue(true)
};

При изменении структуры формы версия позволяет выполнять миграцию данных.


Синхронизация нескольких Choices-инстансов

В формах с несколькими селектами состояние может зависеть друг от друга. Тогда используется единое хранилище:

const globalState = {
  country: [],
  city: []
};

И синхронизация через общий обработчик событий:

function updateState(name, values) {
  globalState[name] = values;
}

Поведение при повторной инициализации

При повторной инициализации компонента важно избегать наложения старого состояния:

  • очистка DOM
  • удаление старых экземпляров
  • повторная установка данных только после init
choices.destroy();
choices = new Choices(select, options);

Хранение состояния поиска и фильтрации

Choices поддерживает поиск внутри списка. При необходимости можно сохранять:

  • введенный текст поиска
  • активный фильтр
  • позицию прокрутки списка
sessionStorage.setItem('search', input.value);

Итоговая модель жизненного цикла состояния

Система сохранения состояния в Choices.js обычно включает:

  • захват состояния через события
  • сериализацию в JSON
  • хранение в sessionStorage/localStorage
  • восстановление после инициализации
  • валидацию соответствия данных
  • синхронизацию с сервером или SPA-стором