Reducers

Reducers в Kepler.gl построены поверх архитектуры Redux и определяют единственный механизм изменения состояния приложения. Вся модель данных библиотеки сводится к иммутабельным преобразованиям, где каждый редьюсер отвечает за строго ограниченный фрагмент глобального состояния: карты, слоёв, датасетов и интерфейса. Такая организация обеспечивает предсказуемость обновлений и упрощает интеграцию с внешними источниками данных и кастомными расширениями.

Внутренняя структура Kepler.gl основана на разделении состояния на несколько доменов:

  • visState — визуальное представление данных (слои, фильтры, взаимодействия)
  • mapState — параметры карты (центр, масштаб, проекция)
  • uiState — состояние интерфейса (панели, модальные окна, настройки)
  • mapStyle — стилизация карты
  • datasets — наборы данных и их метаданные

Каждый из этих сегментов обслуживается отдельным reducer-ом, а итоговый root reducer собирается через combineReducers. Это позволяет изолировать логику изменения состояния и минимизировать побочные эффекты.

Основной принцип иммутабельности

Reducers Kepler.gl строго придерживаются принципа неизменяемости. Вместо модификации существующих объектов создаются новые структуры данных:

  • объекты копируются через spread-операторы или специализированные утилиты
  • массивы обновляются через map, filter, concat
  • вложенные структуры пересобираются полностью при изменении глубинных полей

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

Структура visState reducer

visState является наиболее сложным сегментом состояния. Он управляет визуализацией данных и включает:

  • layers — список визуальных слоёв (scatterplot, hexagon, arc и др.)
  • filters — активные фильтры данных
  • interactionConfig — настройки взаимодействия (tooltip, brush, click)
  • animationConfig — параметры временной анимации

Reducer visState обрабатывает широкий набор action-ов:

  • добавление и удаление слоёв
  • изменение конфигурации визуализации
  • обновление фильтров
  • синхронизацию данных с датасетами

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

Пример логики обновления слоя

При изменении параметров слоя (например, радиуса точек) reducer выполняет:

  1. Поиск слоя по уникальному идентификатору
  2. Создание копии массива layers
  3. Замена изменённого слоя новым объектом
  4. Возврат нового visState

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

mapState reducer и геометрия карты

mapState отвечает за географическое положение и камеру визуализации:

  • latitude и longitude центра карты
  • zoom уровень
  • bearing и pitch
  • dragRotate и другие параметры взаимодействия

Reducer mapState работает с действиями перемещения карты и изменением viewport. Важным аспектом является синхронизация с внешними библиотеками картографии, такими как Mapbox GL, где состояние камеры должно обновляться атомарно.

Особенность реализации заключается в том, что mapState часто обновляется с высокой частотой (например, при панорамировании), поэтому reducer оптимизирован для минимального количества аллокаций объектов.

uiState reducer и управление интерфейсом

uiState содержит состояние пользовательского интерфейса:

  • активные панели (sidebar, filter panel, layer panel)
  • модальные окна
  • уведомления
  • режимы взаимодействия

Reducer uiState отличается более простой структурой по сравнению с visState, но включает множество булевых флагов и строковых идентификаторов. Основная задача — обеспечить синхронизацию UI с текущим режимом работы карты без вмешательства в данные визуализации.

mapStyle reducer и стилизация

mapStyle управляет визуальным стилем карты:

  • выбор базовой карты (light, dark, satellite)
  • кастомные стили Mapbox
  • настройка слоёв фона
  • конфигурация цветовых схем

Reducer обновляет конфигурацию стиля через замену объектов style и styleType. При этом часто используется механизм глубокого клонирования, поскольку стили могут содержать вложенные JSON-структуры.

datasets reducer и управление данными

datasets reducer отвечает за загрузку, хранение и обновление источников данных. Каждый dataset включает:

  • raw data (исходные записи)
  • processed data (обработанные данные для визуализации)
  • metadata (названия полей, типы данных)
  • color domain и статистические параметры

Reducer обрабатывает:

  • добавление нового датасета
  • обновление существующего набора данных
  • удаление источника
  • синхронизацию с visState

Особенность заключается в тесной связи с visState: изменения данных часто триггерят перерасчёт слоёв и фильтров.

Комбинирование reducers

Все reducers объединяются в единый root reducer:

  • visState reducer
  • mapState reducer
  • uiState reducer
  • mapStyle reducer
  • datasets reducer

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

Обработка actions в reducers

Actions в Kepler.gl представляют собой описания событий:

  • добавление данных
  • изменение фильтров
  • обновление viewport
  • переключение стилей

Reducer принимает action и проверяет его тип. В зависимости от типа выполняется соответствующая трансформация состояния. Внутренне используется pattern switch(action.type).

При этом часть логики вынесена в утилиты и helpers, чтобы reducers оставались максимально чистыми и предсказуемыми.

Иммутабельные утилиты и оптимизация

Для работы с глубоко вложенными структурами Kepler.gl использует вспомогательные функции:

  • safe update объектов
  • копирование массивов слоёв
  • обновление вложенных конфигураций
  • merge состояния

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

Оптимизация reducers достигается за счёт:

  • минимизации пересоздания объектов
  • избирательного обновления слоёв
  • мемоизации вычислений на уровне селекторов

Связь reducers и селекторов

Reducers формируют состояние, которое затем используется селекторами для вычислений. Селекторы извлекают:

  • активные слои
  • отфильтрованные данные
  • параметры визуализации

Благодаря иммутабельной природе reducers, селекторы могут эффективно определять изменения и избегать лишних перерасчётов.

Кастомизация reducers

Kepler.gl допускает расширение reducers через middleware и custom enhancers. Возможны сценарии:

  • добавление новых типов слоёв
  • модификация логики обработки данных
  • интеграция с внешними состояниями Redux

Кастомный reducer может быть объединён с базовыми через combineReducers, сохраняя совместимость с ядром библиотеки.

Поток обновления состояния

Типичный цикл работы reducers выглядит следующим образом:

  1. Действие инициируется (например, изменение фильтра)
  2. Action попадает в store
  3. Соответствующий reducer обрабатывает action
  4. Возвращается новое состояние
  5. Селекторы пересчитывают производные данные
  6. UI обновляется

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

Влияние архитектуры reducers на масштабируемость

Разделение состояния на независимые reducers позволяет:

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

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