Сохранение состояния в localStorage

Сохранение состояния выпадающих списков, созданных на базе Slim Select, позволяет восстанавливать выбор пользователя после перезагрузки страницы, переходов между разделами и повторного открытия интерфейса. Основной механизм основан на синхронизации выбранных значений с localStorage и последующем восстановлении состояния при инициализации компонента.

Базовая модель хранения состояния

Slim Select работает поверх стандартного <select> элемента, поэтому состояние фактически сводится к набору выбранных value. Эти значения удобно сериализовать в строку и сохранять в браузере.

Ключевой принцип:

  • извлечение выбранных значений через API Slim Select
  • сериализация в JSON
  • сохранение в localStorage
  • восстановление перед или сразу после инициализации

Простейшая схема хранения:

const STORAGE_KEY = 'slimselect:country';

function saveState(values) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(values));
}

function loadState() {
  const raw = localStorage.getItem(STORAGE_KEY);
  return raw ? JSON.parse(raw) : [];
}

Инициализация Slim Select с восстановлением состояния

При создании экземпляра Slim Select важно учитывать момент, когда значения уже восстановлены из localStorage. Обычно восстановление выполняется до или сразу после инициализации.

const selectEl = document.querySelector('#country');

const savedValues = loadState();

const slim = new SlimSelect({
  select: selectEl
});

if (savedValues.length) {
  slim.set(savedValues);
}

Метод set позволяет программно задать выбранные значения, что делает его ключевым элементом восстановления состояния.

Синхронизация изменений

Для постоянного обновления состояния используется событие изменения выбора. Slim Select предоставляет callback onChange, который вызывается при каждом изменении значения.

const slim = new SlimSelect({
  select: '#country',
  onChange: (info) => {
    const values = info.map(item => item.value);
    saveState(values);
  }
});

Здесь info содержит массив выбранных элементов, каждый из которых включает value, text и дополнительные метаданные.

Обработка одиночного и множественного выбора

Поведение сохранения зависит от режима multiple.

Одиночный выбор

В случае обычного <select> сохраняется одно значение:

onChange: (info) => {
  const value = info ? info.value : null;
  localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
}

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

const saved = JSON.parse(localStorage.getItem(STORAGE_KEY));

if (saved) {
  slim.set(saved);
}

Множественный выбор

При multiple сохраняется массив:

onChange: (info) => {
  const values = info.map(i => i.value);
  localStorage.setItem(STORAGE_KEY, JSON.stringify(values));
}

Восстановление аналогично:

const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
slim.set(saved);

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

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

const STORAGE_KEYS = {
  country: 'slimselect:country',
  city: 'slimselect:city',
  language: 'slimselect:language'
};

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

function createPersistedSelect(selector, key) {
  const slim = new SlimSelect({
    select: selector,
    onChange: (info) => {
      const values = Array.isArray(info)
        ? info.map(i => i.value)
        : info?.value;

      localStorage.setItem(key, JSON.stringify(values));
    }
  });

  const saved = JSON.parse(localStorage.getItem(key) || 'null');

  if (saved) {
    slim.set(saved);
  }

  return slim;
}

createPersistedSelect('#country', STORAGE_KEYS.country);
createPersistedSelect('#city', STORAGE_KEYS.city);

Обработка динамических данных

Если список опций загружается асинхронно (например, через API), восстановление состояния должно выполняться после загрузки данных, иначе Slim Select не сможет сопоставить значения.

const slim = new SlimSelect({
  select: '#country',
  data: []
});

fetch('/api/countries')
  .then(res => res.json())
  .then(options => {
    slim.setData(options);

    const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
    slim.set(saved);
  });

Здесь критически важно соблюдать порядок:

  1. загрузка данных
  2. установка setData
  3. восстановление set

Сериализация сложных значений

Если значения представляют собой не строки, а объекты (например, { id, name }), хранить в localStorage приходится только идентификаторы.

onChange: (info) => {
  const ids = info.map(i => i.value.id);
  localStorage.setItem(STORAGE_KEY, JSON.stringify(ids));
}

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

const savedIds = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');

const matched = options.filter(opt =>
  savedIds.includes(opt.value.id)
);

slim.set(matched.map(m => m.value));

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

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

localStorage.removeItem(STORAGE_KEY);
slim.set([]);

Дополнительно возможно принудительное обновление UI:

slim.setData(slim.data);
slim.set([]);

Версионирование сохранённых данных

При изменении структуры данных (например, переход с string на объект) старые значения могут стать несовместимыми. Для предотвращения конфликтов используется версия ключа.

const STORAGE_KEY = 'slimselect:country:v2';

Или более гибкий вариант:

const STORAGE_KEY = `slimselect:country:${APP_VERSION}`;

При смене версии старые данные автоматически игнорируются.

Обработка ошибок и защита от некорректных данных

localStorage может содержать повреждённые данные, поэтому требуется безопасный парсинг.

function safeLoad(key) {
  try {
    return JSON.parse(localStorage.getItem(key)) || [];
  } catch (e) {
    return [];
  }
}

Использование:

const saved = safeLoad(STORAGE_KEY);
slim.set(saved);

Поведение при отсутствии совпадений

Если сохранённые значения отсутствуют в текущем списке опций, Slim Select игнорирует их. Это важно учитывать при фильтрации данных с сервера.

Практика обработки:

const validValues = saved.filter(v =>
  options.some(opt => opt.value === v)
);

slim.set(validValues);

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

При использовании Slim Select внутри формы важно синхронизировать localStorage с отправкой формы, чтобы не возникало расхождений между UI и отправляемыми данными.

form.addEventListener('submit', () => {
  const values = slim.selected();
  localStorage.setItem(STORAGE_KEY, JSON.stringify(values));
});

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

Оптимизация частоты записи

При частых изменениях выбора (особенно в режиме multiple) запись в localStorage может выполняться слишком часто. Используется простое ограничение через debounce.

let timeout;

function debounceSave(values) {
  clearTimeout(timeout);

  timeout = setTimeout(() => {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(values));
  }, 200);
}

Использование:

onChange: (info) => {
  const values = info.map(i => i.value);
  debounceSave(values);
}

Согласованность состояния между вкладками

localStorage не синхронизирует изменения автоматически между вкладками. Для этого используется событие storage.

window.addEventListener('storage', (e) => {
  if (e.key === STORAGE_KEY) {
    const values = JSON.parse(e.newValue || '[]');
    slim.set(values);
  }
});

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