Подключение к существующему Redux store

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

Ключевая особенность заключается в том, что Kepler.gl не «подключается как обычный React-компонент», а становится полноценным участником Redux-архитектуры, где его состояние хранится в отдельном поддереве и синхронизируется через действия (actions).


Структура состояния Kepler.gl внутри Redux

После подключения Kepler.gl редьюсера в store, состояние приобретает следующую форму:

state = {
  keplerGl: {
    map1: {
      visState: {...},
      mapState: {...},
      uiState: {...}
    },
    map2: {
      visState: {...},
      mapState: {...},
      uiState: {...}
    }
  },
  ...другие редьюсеры приложения
}

Основные части состояния

visState

  • слои (layers)
  • датасеты
  • фильтры
  • взаимодействия с данными

mapState

  • центр карты (latitude, longitude)
  • zoom
  • pitch
  • bearing

uiState

  • состояние интерфейса (панели, модальные окна)
  • режимы отображения
  • настройки UI-компонентов

Важно понимать: Kepler.gl допускает несколько независимых экземпляров карт, каждый из которых идентифицируется ключом (map1, dashboard, и т.д.). Этот ключ должен совпадать с id компонента KeplerGl.


Подключение редьюсера Kepler.gl к существующему store

В уже существующем Redux store необходимо аккуратно добавить редьюсер Kepler.gl через combineReducers.

Базовый пример интеграции

import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import keplerGlReducer from '@kepler.gl/reducers';

import appReducer from './reducers/app';

const rootReducer = combineReducers({
  app: appReducer,
  keplerGl: keplerGlReducer
});

const store = createStore(
  rootReducer,
  compose(
    applyMiddleware()
  )
);

На этом уровне Kepler.gl уже становится частью глобального состояния приложения.


Инициализация состояния Kepler.gl

Kepler.gl предоставляет возможность задавать начальное состояние редьюсера. Это особенно важно при интеграции в существующее приложение, где требуется контроль над дефолтными параметрами интерфейса и карты.

import keplerGlReducer from '@kepler.gl/reducers';

const customizedKeplerGlReducer = keplerGlReducer.initialState({
  uiState: {
    readOnly: false,
    currentModal: null
  },
  mapState: {
    latitude: 50.4501,
    longitude: 30.5234,
    zoom: 10
  }
});

После этого редьюсер подключается вместо стандартного:

const rootReducer = combineReducers({
  app: appReducer,
  keplerGl: customizedKeplerGlReducer
});

Использование ключа экземпляра карты (mapId)

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

  • ключом в Redux store
  • prop id компонента KeplerGl

Пример

<KeplerGl
  id="map1"
  width={width}
  height={height}
/>

И соответствующая структура state:

state.keplerGl.map1

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


Подключение Kepler.gl в уже существующее приложение React-Redux

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

import { Provider } from 'react-redux';
import store from './store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

После этого можно подключать сам компонент карты:

import KeplerGl from '@kepler.gl/components';

function MapView() {
  return (
    <KeplerGl
      id="map1"
      width={window.innerWidth}
      height={window.innerHeight}
    />
  );
}

Встраивание Kepler.gl в существующую структуру reducers

В реальных проектах Redux store редко состоит только из combineReducers. Часто используется модульная архитектура с динамическими редьюсерами.

Пример модульной интеграции

const staticReducers = {
  app: appReducer
};

const createRootReducer = (asyncReducers) =>
  combineReducers({
    ...staticReducers,
    ...asyncReducers,
    keplerGl: keplerGlReducer
  });

Такой подход позволяет:

  • добавлять Kepler.gl без переписывания архитектуры
  • динамически расширять store
  • сохранять совместимость с code splitting

Динамическое добавление редьюсера Kepler.gl

В приложениях с динамической загрузкой модулей часто требуется добавлять Kepler.gl редьюсер после инициализации store.

store.asyncReducers.keplerGl = keplerGlReducer;
store.replaceReducer(createRootReducer(store.asyncReducers));

Это позволяет внедрять Kepler.gl только при необходимости, например, при переходе на страницу аналитики.


Middleware Kepler.gl и его роль

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

Подключение middleware

import { createStore, applyMiddleware } from 'redux';
import keplerGlMiddleware from '@kepler.gl/middleware';

const store = createStore(
  rootReducer,
  applyMiddleware(
    keplerGlMiddleware
  )
);

Middleware обеспечивает:

  • корректную обработку действий добавления данных
  • синхронизацию между слоями и UI
  • управление асинхронными операциями

Добавление данных в Kepler.gl через Redux actions

Основной способ взаимодействия с картой — dispatch действий.

Импорт действий

import { addDataToMap } from '@kepler.gl/actions';

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

store.dispatch(
  addDataToMap({
    datasets: {
      info: {
        label: 'Cities',
        id: 'cities'
      },
      data: [
        { lat: 50.45, lng: 30.52 },
        { lat: 48.38, lng: 31.17 }
      ]
    },
    options: {
      centerMap: true,
      readOnly: false
    },
    config: {}
  })
);

Данные автоматически попадают в visState соответствующего mapId.


Синхронизация Kepler.gl с существующим состоянием приложения

В реальных системах часто требуется связать Kepler.gl с уже существующими данными Redux, например:

  • фильтры бизнес-логики
  • пользовательские настройки
  • данные API

Пример селектора

const selectKeplerFilters = (state, mapId) =>
  state.keplerGl[mapId]?.visState.filters;

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

const mapStateToProps = (state) => ({
  filters: state.keplerGl.map1.visState.filters,
  theme: state.app.theme
});

Типичные ошибки интеграции

1. Несовпадение id карты

Если компонент использует:

id="map1"

а store содержит:

state.keplerGl.defaultMap

Kepler.gl создаст новое состояние, что приведёт к потере данных.


2. Потеря состояния при пересоздании store

При hot reload или SSR необходимо сохранять keplerGl slice отдельно, иначе состояние визуализации будет сбрасываться.


3. Конфликты middleware

Kepler.gl middleware должен быть добавлен в правильном порядке, особенно если используются:

  • redux-thunk
  • redux-saga
  • custom logging middleware

Рекомендуется размещать keplerGl middleware ближе к началу цепочки.


Работа с несколькими картами в одном store

Kepler.gl поддерживает несколько экземпляров одновременно.

<KeplerGl id="map1" />
<KeplerGl id="map2" />

Redux state:

state.keplerGl.map1
state.keplerGl.map2

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

  • сравнивать наборы данных
  • строить разные визуализации
  • разделять бизнес-контексты

Интеграция с существующими бизнес-редьюсерами

В сложных приложениях Kepler.gl часто взаимодействует с доменными редьюсерами.

Пример сценария:

  • данные приходят из app.data
  • затем синхронизируются в Kepler.gl
const dataSelector = (state) => state.app.geoData;

const mapDataToKepler = (data) =>
  addDataToMap({
    datasets: {
      info: { label: 'Geo Data', id: 'geo' },
      data
    }
  });

Контроль состояния через Redux DevTools

Интеграция Kepler.gl автоматически раскрывает внутреннее состояние в DevTools:

  • visState
  • mapState
  • uiState
  • datasets

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

  • отлаживать фильтры
  • проверять слои
  • отслеживать изменения карты в реальном времени

Особенности производительной интеграции

При работе с большими датасетами важно учитывать:

  • частые dispatch addDataToMap могут блокировать UI
  • следует минимизировать перерендеры Redux store
  • рекомендуется использовать мемоизацию селекторов (reselect)
import { createSelector } from 'reselect';

const selectMapState = (state) => state.keplerGl.map1;

export const selectVisibleLayers = createSelector(
  selectMapState,
  (map) => map.visState.layers
);

Синхронизация внешнего состояния с Kepler.gl

Kepler.gl не запрещает внешнее управление состоянием карты. Это означает, что можно:

  • программно менять центр карты
  • обновлять фильтры
  • переключать слои

через стандартные Redux actions, сохраняя единый поток данных внутри приложения.