Kepler.gl построен вокруг концепции централизованного состояния визуализации, полностью управляемого через Redux. Это означает, что любая интеграция с уже существующим приложением требует аккуратного встраивания редьюсера Kepler.gl в общий store без нарушения структуры приложения и без конфликта с уже существующими слайсами состояния.
Ключевая особенность заключается в том, что Kepler.gl не «подключается как обычный React-компонент», а становится полноценным участником Redux-архитектуры, где его состояние хранится в отдельном поддереве и синхронизируется через действия (actions).
После подключения Kepler.gl редьюсера в store, состояние приобретает следующую форму:
state = {
keplerGl: {
map1: {
visState: {...},
mapState: {...},
uiState: {...}
},
map2: {
visState: {...},
mapState: {...},
uiState: {...}
}
},
...другие редьюсеры приложения
}
visState
mapState
uiState
Важно понимать: Kepler.gl допускает несколько независимых экземпляров
карт, каждый из которых идентифицируется ключом (map1,
dashboard, и т.д.). Этот ключ должен совпадать с
id компонента KeplerGl.
В уже существующем 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 предоставляет возможность задавать начальное состояние редьюсера. Это особенно важно при интеграции в существующее приложение, где требуется контроль над дефолтными параметрами интерфейса и карты.
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
});
Одной из критически важных особенностей является необходимость строгого соответствия между:
id компонента KeplerGl<KeplerGl
id="map1"
width={width}
height={height}
/>
И соответствующая структура state:
state.keplerGl.map1
Любое несоответствие приведёт к тому, что компонент не сможет найти своё состояние или создаст новый изолированный экземпляр.
В существующем приложении обычно уже используется
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}
/>
);
}
В реальных проектах Redux store редко состоит только из
combineReducers. Часто используется модульная архитектура с
динамическими редьюсерами.
const staticReducers = {
app: appReducer
};
const createRootReducer = (asyncReducers) =>
combineReducers({
...staticReducers,
...asyncReducers,
keplerGl: keplerGlReducer
});
Такой подход позволяет:
В приложениях с динамической загрузкой модулей часто требуется добавлять Kepler.gl редьюсер после инициализации store.
store.asyncReducers.keplerGl = keplerGlReducer;
store.replaceReducer(createRootReducer(store.asyncReducers));
Это позволяет внедрять Kepler.gl только при необходимости, например, при переходе на страницу аналитики.
Kepler.gl использует middleware для обработки сложных побочных эффектов, связанных с визуализацией и загрузкой данных.
import { createStore, applyMiddleware } from 'redux';
import keplerGlMiddleware from '@kepler.gl/middleware';
const store = createStore(
rootReducer,
applyMiddleware(
keplerGlMiddleware
)
);
Middleware обеспечивает:
Основной способ взаимодействия с картой — 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 с уже существующими данными Redux, например:
const selectKeplerFilters = (state, mapId) =>
state.keplerGl[mapId]?.visState.filters;
const mapStateToProps = (state) => ({
filters: state.keplerGl.map1.visState.filters,
theme: state.app.theme
});
Если компонент использует:
id="map1"
а store содержит:
state.keplerGl.defaultMap
Kepler.gl создаст новое состояние, что приведёт к потере данных.
При hot reload или SSR необходимо сохранять keplerGl
slice отдельно, иначе состояние визуализации будет сбрасываться.
Kepler.gl middleware должен быть добавлен в правильном порядке, особенно если используются:
Рекомендуется размещать keplerGl middleware ближе к началу цепочки.
Kepler.gl поддерживает несколько экземпляров одновременно.
<KeplerGl id="map1" />
<KeplerGl id="map2" />
Redux state:
state.keplerGl.map1
state.keplerGl.map2
Это позволяет:
В сложных приложениях Kepler.gl часто взаимодействует с доменными редьюсерами.
Пример сценария:
app.dataconst dataSelector = (state) => state.app.geoData;
const mapDataToKepler = (data) =>
addDataToMap({
datasets: {
info: { label: 'Geo Data', id: 'geo' },
data
}
});
Интеграция Kepler.gl автоматически раскрывает внутреннее состояние в DevTools:
Это позволяет:
При работе с большими датасетами важно учитывать:
addDataToMap могут блокировать UIimport { createSelector } from 'reselect';
const selectMapState = (state) => state.keplerGl.map1;
export const selectVisibleLayers = createSelector(
selectMapState,
(map) => map.visState.layers
);
Kepler.gl не запрещает внешнее управление состоянием карты. Это означает, что можно:
через стандартные Redux actions, сохраняя единый поток данных внутри приложения.