Skip to content

Commit f5be388

Browse files
committed
improve state management
1 parent 8bcf457 commit f5be388

File tree

5 files changed

+123
-71
lines changed

5 files changed

+123
-71
lines changed

packages/map-gl-native/src/components/Map.js

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
import React, { useMemo, useContext, useRef, useCallback } from "react";
1+
import React, { useMemo, useContext, useRef, useState, useEffect } from "react";
22
import { MapView, Camera } from "@maplibre/maplibre-react-native";
33
import { MapContext } from "./MapProvider.js";
44
import { withWQ } from "@wq/react";
55
import { useBasemapStyle } from "../hooks.js";
66
import PropTypes from "prop-types";
77

8-
function Map({
9-
name,
10-
initBounds,
11-
children,
12-
containerStyle,
13-
basemap,
14-
...mapProps
15-
}) {
8+
function Map({ initBounds, children, containerStyle, basemap, ...mapProps }) {
169
const { setInstance } = useContext(MapContext),
1710
fitBounds = useMemo(() => {
11+
if (!initBounds) {
12+
return null;
13+
}
1814
const [[xmin, ymin], [xmax, ymax]] = initBounds;
1915
return { sw: [xmin, ymin], ne: [xmax, ymax] };
2016
}, [initBounds]),
@@ -29,27 +25,12 @@ function Map({
2925

3026
const mapRef = useRef(),
3127
cameraRef = useRef(),
32-
mapInstance = useMemo(() => createMapInstance(mapRef, cameraRef), []),
33-
onInit = useCallback(
34-
() => setInstance(mapInstance, name),
35-
[setInstance, mapInstance]
36-
),
37-
onPress = useCallback(
38-
(e) => {
39-
if (mapInstance.handlers.click) {
40-
mapInstance.handlers.click(e);
41-
}
42-
},
43-
[mapInstance]
28+
[cameraProps, setCameraProps] = useState({}),
29+
mapInstance = useMemo(
30+
() => createMapInstance(mapRef, cameraRef, setCameraProps),
31+
[]
4432
),
45-
onMove = useCallback(
46-
(e) => {
47-
if (mapInstance.handlers.move) {
48-
mapInstance.handlers.move(e);
49-
}
50-
},
51-
[mapInstance]
52-
);
33+
{ handleClick, handleMove } = mapInstance;
5334

5435
let rotateEnabled, pitchEnabled;
5536
if (mapProps.rotateEnabled !== undefined) {
@@ -67,6 +48,11 @@ function Map({
6748
pitchEnabled = rotateEnabled;
6849
}
6950

51+
useEffect(() => {
52+
setInstance(mapInstance);
53+
return () => setInstance(null);
54+
}, [mapRef.current]);
55+
7056
return (
7157
<MapView
7258
ref={mapRef}
@@ -76,12 +62,17 @@ function Map({
7662
attributionEnabled={false}
7763
logoEnabled={false}
7864
style={style}
79-
onPress={onPress}
80-
onWillStartLoadingMap={onInit}
81-
onRegionDidChange={onMove}
65+
onPress={handleClick}
66+
onRegionDidChange={handleMove}
8267
{...mapProps}
8368
>
84-
<Camera ref={cameraRef} bounds={fitBounds} animationDuration={0} />
69+
<Camera
70+
ref={cameraRef}
71+
defaultSettings={{
72+
bounds: fitBounds,
73+
}}
74+
{...cameraProps}
75+
/>
8576
{children}
8677
</MapView>
8778
);
@@ -98,17 +89,36 @@ Map.propTypes = {
9889
export default withWQ(Map);
9990

10091
// Mimic some functions from maplibre-gl-js & react-map-gl
101-
export function createMapInstance(mapRef, cameraRef) {
102-
return {
103-
handlers: {},
92+
export function createMapInstance(mapRef, cameraRef, setCameraProps) {
93+
const instance = {
94+
handlers: {
95+
click: [],
96+
move: [],
97+
},
10498
on(event, handler) {
105-
if (!["click", "move"].includes(event)) {
99+
if (!(event in this.handlers)) {
106100
console.warn("Unsupported event: " + event);
107101
}
108-
this.handlers[event] = handler;
102+
this.handlers[event].push(handler);
109103
},
110-
off(event) {
111-
delete this.handlers[event];
104+
off(event, handler) {
105+
if (!(event in this.handlers)) {
106+
console.warn("Unsupported event: " + event);
107+
}
108+
this.handlers[event] = this.handlers[event].filter(
109+
(h) => h !== handler
110+
);
111+
},
112+
_runHandlers(event, e) {
113+
for (const handler of this.handlers[event]) {
114+
handler(e);
115+
}
116+
},
117+
handleClick(e) {
118+
this._runHandlers("click", e);
119+
},
120+
handleMove(e) {
121+
this._runHandlers("move", e);
112122
},
113123
flyTo({ center, zoom }) {
114124
this.getCamera()?.flyTo(center, zoom);
@@ -130,5 +140,11 @@ export function createMapInstance(mapRef, cameraRef) {
130140
getCamera() {
131141
return cameraRef.current;
132142
},
143+
setCameraProps,
133144
};
145+
146+
instance.handleClick = instance.handleClick.bind(instance);
147+
instance.handleMove = instance.handleMove.bind(instance);
148+
149+
return instance;
134150
}
Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,27 @@
11
import { MapProvider } from "react-map-gl/maplibre";
22
import { withWQ } from "@wq/react";
3+
import Map from "./Map.js";
4+
import MapInteraction from "./MapInteraction.js";
5+
import MapAutoZoom from "./MapAutoZoom.js";
6+
import MapIdentify from "./MapIdentify.js";
7+
import HighlightPopup from "./HighlightPopup.js";
8+
import Geojson from "../overlays/Geojson.js";
9+
import Tile from "../overlays/Tile.js";
10+
import VectorTile from "../overlays/VectorTile.js";
11+
import Highlight from "../overlays/Highlight.js";
312

4-
export default withWQ(MapProvider);
13+
const MapProviderDefaults = {
14+
components: {
15+
Map,
16+
MapInteraction,
17+
MapAutoZoom,
18+
MapIdentify,
19+
HighlightPopup,
20+
Geojson,
21+
Tile,
22+
VectorTile,
23+
Highlight,
24+
},
25+
};
26+
27+
export default withWQ(MapProvider, { defaults: MapProviderDefaults });

packages/map/src/components/AutoMap.js

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ export const AutoMapDefaults = {
6161
function AutoMap({
6262
name,
6363
mapId,
64-
toolbar = true,
64+
toolbar: Toolbar = true,
6565
toolbarAnchor = "top-right",
6666
containerStyle,
6767
context = {},
68-
overlays: initialOverlays = [],
68+
overlays: initialOverlays = null,
6969
basemaps: initialBasemaps = null,
7070
initBounds: initialInitBounds = null,
7171
tiles: initialTiles = null,
@@ -104,8 +104,9 @@ function AutoMap({
104104
onChangeBasemap,
105105
onChangeOverlays
106106
),
107-
[state, actions] = reducer,
108-
{
107+
[state, actions] = reducer;
108+
109+
const {
109110
MapContainer,
110111
MapToolbar,
111112
Map,
@@ -143,26 +144,31 @@ function AutoMap({
143144

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

146-
if (toolbar === true) {
147-
toolbar = (
148-
<MapToolbar
149-
name={name}
150-
mapId={mapId}
151-
basemaps={basemaps}
152-
overlays={overlays}
153-
showOverlay={showOverlay}
154-
hideOverlay={hideOverlay}
155-
setBasemap={setBasemap}
156-
context={context}
157-
anchor={toolbarAnchor}
158-
/>
159-
);
160-
} else if (!toolbar) {
161-
toolbar = false;
162-
}
147+
const toolbar = (() => {
148+
const toolbarProps = {
149+
name,
150+
mapId,
151+
basemaps,
152+
overlays,
153+
showOverlay,
154+
hideOverlay,
155+
setBasemap,
156+
context,
157+
anchor: toolbarAnchor,
158+
};
159+
if (Toolbar === true) {
160+
return <MapToolbar {...toolbarProps} />;
161+
} else if (typeof Toolbar === "function") {
162+
return <Toolbar {...toolbarProps} />;
163+
} else if (!Toolbar) {
164+
return false;
165+
} else {
166+
return Toolbar;
167+
}
168+
})();
163169

164170
return (
165-
<MapReducerProvider value={reducer}>
171+
<MapReducerProvider state={state} actions={actions}>
166172
<MapContainer name={name} mapId={mapId}>
167173
{toolbarAnchor.endsWith("left") && toolbar}
168174
<Map

packages/map/src/hooks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export function useRootMapReducer(
2424
const [state, dispatch] = useReducer(
2525
(state, action) => reducer(state, action),
2626
{
27-
basemaps,
28-
overlays,
27+
basemaps: basemaps || [],
28+
overlays: overlays || [],
2929
initBounds,
3030
tiles,
3131
autoZoom,

packages/map/src/reducer.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,21 @@ const emptyState = {
1515
basemaps: [],
1616
overlays: [],
1717
viewState: null,
18-
initBounds: null,
18+
initBounds: undefined,
1919
tiles: null,
2020
autoZoom: null,
2121
highlight: null,
2222
mapId: null,
2323
activeBasemap: null,
24-
activeOverlays: [],
24+
activeOverlays: null,
2525
};
2626

2727
export default function reducer(state = emptyState, action) {
2828
switch (action.type) {
2929
case MAP_INITIALIZE: {
3030
const {
31-
basemaps,
32-
overlays,
31+
basemaps: initBasemaps,
32+
overlays: initOverlays,
3333
viewState,
3434
initBounds,
3535
tiles,
@@ -39,9 +39,11 @@ export default function reducer(state = emptyState, action) {
3939
activeBasemap,
4040
activeOverlays,
4141
} = { ...emptyState, ...action.payload };
42+
const basemaps = reduceBasemaps([], initBasemaps, activeBasemap),
43+
overlays = reduceOverlays([], initOverlays, activeOverlays);
4244
return {
43-
basemaps: reduceBasemaps([], basemaps, activeBasemap),
44-
overlays: reduceOverlays([], overlays, activeOverlays),
45+
basemaps,
46+
overlays,
4547
viewState,
4648
initBounds,
4749
tiles,
@@ -225,6 +227,11 @@ function reduceOverlays(lastOverlays, nextOverlays, activeOverlays) {
225227
...overlay,
226228
active: true,
227229
};
230+
} else if (activeOverlays && !activeOverlays.includes(overlay.name)) {
231+
return {
232+
...overlay,
233+
active: false,
234+
};
228235
} else if (overlay.active) {
229236
return {
230237
...overlay,

0 commit comments

Comments
 (0)