Skip to content

Commit

Permalink
improve state management
Browse files Browse the repository at this point in the history
  • Loading branch information
sheppard committed Feb 9, 2025
1 parent 8bcf457 commit f5be388
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 71 deletions.
96 changes: 56 additions & 40 deletions packages/map-gl-native/src/components/Map.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import React, { useMemo, useContext, useRef, useCallback } from "react";
import React, { useMemo, useContext, useRef, useState, useEffect } from "react";
import { MapView, Camera } from "@maplibre/maplibre-react-native";
import { MapContext } from "./MapProvider.js";
import { withWQ } from "@wq/react";
import { useBasemapStyle } from "../hooks.js";
import PropTypes from "prop-types";

function Map({
name,
initBounds,
children,
containerStyle,
basemap,
...mapProps
}) {
function Map({ initBounds, children, containerStyle, basemap, ...mapProps }) {
const { setInstance } = useContext(MapContext),
fitBounds = useMemo(() => {
if (!initBounds) {
return null;
}
const [[xmin, ymin], [xmax, ymax]] = initBounds;
return { sw: [xmin, ymin], ne: [xmax, ymax] };
}, [initBounds]),
Expand All @@ -29,27 +25,12 @@ function Map({

const mapRef = useRef(),
cameraRef = useRef(),
mapInstance = useMemo(() => createMapInstance(mapRef, cameraRef), []),
onInit = useCallback(
() => setInstance(mapInstance, name),
[setInstance, mapInstance]
),
onPress = useCallback(
(e) => {
if (mapInstance.handlers.click) {
mapInstance.handlers.click(e);
}
},
[mapInstance]
[cameraProps, setCameraProps] = useState({}),
mapInstance = useMemo(
() => createMapInstance(mapRef, cameraRef, setCameraProps),
[]
),
onMove = useCallback(
(e) => {
if (mapInstance.handlers.move) {
mapInstance.handlers.move(e);
}
},
[mapInstance]
);
{ handleClick, handleMove } = mapInstance;

let rotateEnabled, pitchEnabled;
if (mapProps.rotateEnabled !== undefined) {
Expand All @@ -67,6 +48,11 @@ function Map({
pitchEnabled = rotateEnabled;
}

useEffect(() => {
setInstance(mapInstance);
return () => setInstance(null);
}, [mapRef.current]);

return (
<MapView
ref={mapRef}
Expand All @@ -76,12 +62,17 @@ function Map({
attributionEnabled={false}
logoEnabled={false}
style={style}
onPress={onPress}
onWillStartLoadingMap={onInit}
onRegionDidChange={onMove}
onPress={handleClick}
onRegionDidChange={handleMove}
{...mapProps}
>
<Camera ref={cameraRef} bounds={fitBounds} animationDuration={0} />
<Camera
ref={cameraRef}
defaultSettings={{
bounds: fitBounds,
}}
{...cameraProps}
/>
{children}
</MapView>
);
Expand All @@ -98,17 +89,36 @@ Map.propTypes = {
export default withWQ(Map);

// Mimic some functions from maplibre-gl-js & react-map-gl
export function createMapInstance(mapRef, cameraRef) {
return {
handlers: {},
export function createMapInstance(mapRef, cameraRef, setCameraProps) {
const instance = {
handlers: {
click: [],
move: [],
},
on(event, handler) {
if (!["click", "move"].includes(event)) {
if (!(event in this.handlers)) {
console.warn("Unsupported event: " + event);
}
this.handlers[event] = handler;
this.handlers[event].push(handler);
},
off(event) {
delete this.handlers[event];
off(event, handler) {
if (!(event in this.handlers)) {
console.warn("Unsupported event: " + event);
}
this.handlers[event] = this.handlers[event].filter(
(h) => h !== handler
);
},
_runHandlers(event, e) {
for (const handler of this.handlers[event]) {
handler(e);
}
},
handleClick(e) {
this._runHandlers("click", e);
},
handleMove(e) {
this._runHandlers("move", e);
},
flyTo({ center, zoom }) {
this.getCamera()?.flyTo(center, zoom);
Expand All @@ -130,5 +140,11 @@ export function createMapInstance(mapRef, cameraRef) {
getCamera() {
return cameraRef.current;
},
setCameraProps,
};

instance.handleClick = instance.handleClick.bind(instance);
instance.handleMove = instance.handleMove.bind(instance);

return instance;
}
25 changes: 24 additions & 1 deletion packages/map-gl-web/src/components/MapProvider.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
import { MapProvider } from "react-map-gl/maplibre";
import { withWQ } from "@wq/react";
import Map from "./Map.js";
import MapInteraction from "./MapInteraction.js";
import MapAutoZoom from "./MapAutoZoom.js";
import MapIdentify from "./MapIdentify.js";
import HighlightPopup from "./HighlightPopup.js";
import Geojson from "../overlays/Geojson.js";
import Tile from "../overlays/Tile.js";
import VectorTile from "../overlays/VectorTile.js";
import Highlight from "../overlays/Highlight.js";

export default withWQ(MapProvider);
const MapProviderDefaults = {
components: {
Map,
MapInteraction,
MapAutoZoom,
MapIdentify,
HighlightPopup,
Geojson,
Tile,
VectorTile,
Highlight,
},
};

export default withWQ(MapProvider, { defaults: MapProviderDefaults });
50 changes: 28 additions & 22 deletions packages/map/src/components/AutoMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export const AutoMapDefaults = {
function AutoMap({
name,
mapId,
toolbar = true,
toolbar: Toolbar = true,
toolbarAnchor = "top-right",
containerStyle,
context = {},
overlays: initialOverlays = [],
overlays: initialOverlays = null,
basemaps: initialBasemaps = null,
initBounds: initialInitBounds = null,
tiles: initialTiles = null,
Expand Down Expand Up @@ -104,8 +104,9 @@ function AutoMap({
onChangeBasemap,
onChangeOverlays
),
[state, actions] = reducer,
{
[state, actions] = reducer;

const {
MapContainer,
MapToolbar,
Map,
Expand Down Expand Up @@ -143,26 +144,31 @@ function AutoMap({

const identify = overlays.some((overlay) => !!overlay.popup);

if (toolbar === true) {
toolbar = (
<MapToolbar
name={name}
mapId={mapId}
basemaps={basemaps}
overlays={overlays}
showOverlay={showOverlay}
hideOverlay={hideOverlay}
setBasemap={setBasemap}
context={context}
anchor={toolbarAnchor}
/>
);
} else if (!toolbar) {
toolbar = false;
}
const toolbar = (() => {
const toolbarProps = {
name,
mapId,
basemaps,
overlays,
showOverlay,
hideOverlay,
setBasemap,
context,
anchor: toolbarAnchor,
};
if (Toolbar === true) {
return <MapToolbar {...toolbarProps} />;
} else if (typeof Toolbar === "function") {
return <Toolbar {...toolbarProps} />;
} else if (!Toolbar) {
return false;
} else {
return Toolbar;
}
})();

return (
<MapReducerProvider value={reducer}>
<MapReducerProvider state={state} actions={actions}>
<MapContainer name={name} mapId={mapId}>
{toolbarAnchor.endsWith("left") && toolbar}
<Map
Expand Down
4 changes: 2 additions & 2 deletions packages/map/src/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export function useRootMapReducer(
const [state, dispatch] = useReducer(
(state, action) => reducer(state, action),
{
basemaps,
overlays,
basemaps: basemaps || [],
overlays: overlays || [],
initBounds,
tiles,
autoZoom,
Expand Down
19 changes: 13 additions & 6 deletions packages/map/src/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ const emptyState = {
basemaps: [],
overlays: [],
viewState: null,
initBounds: null,
initBounds: undefined,
tiles: null,
autoZoom: null,
highlight: null,
mapId: null,
activeBasemap: null,
activeOverlays: [],
activeOverlays: null,
};

export default function reducer(state = emptyState, action) {
switch (action.type) {
case MAP_INITIALIZE: {
const {
basemaps,
overlays,
basemaps: initBasemaps,
overlays: initOverlays,
viewState,
initBounds,
tiles,
Expand All @@ -39,9 +39,11 @@ export default function reducer(state = emptyState, action) {
activeBasemap,
activeOverlays,
} = { ...emptyState, ...action.payload };
const basemaps = reduceBasemaps([], initBasemaps, activeBasemap),
overlays = reduceOverlays([], initOverlays, activeOverlays);
return {
basemaps: reduceBasemaps([], basemaps, activeBasemap),
overlays: reduceOverlays([], overlays, activeOverlays),
basemaps,
overlays,
viewState,
initBounds,
tiles,
Expand Down Expand Up @@ -225,6 +227,11 @@ function reduceOverlays(lastOverlays, nextOverlays, activeOverlays) {
...overlay,
active: true,
};
} else if (activeOverlays && !activeOverlays.includes(overlay.name)) {
return {
...overlay,
active: false,
};
} else if (overlay.active) {
return {
...overlay,
Expand Down

0 comments on commit f5be388

Please sign in to comment.