1
1
import { useEffect , useLayoutEffect } from 'react' ;
2
2
import { MapProps } from '@vis.gl/react-google-maps' ;
3
+ import { InternalCameraStateRef } from './use-internal-camera-state' ;
4
+ import { isLatLngLiteral } from '../../libraries/is-lat-lng-literal' ;
3
5
4
6
/**
5
- * Internal hook to update the map-options and view- parameters when
7
+ * Internal hook to update the map-options and camera parameters when
6
8
* props are changed.
9
+ *
10
+ * @param map the map instance
11
+ * @param cameraStateRef stores the last values seen during dispatch into the
12
+ * react-application in useMapEvents(). We can safely assume that we
13
+ * don't need to feed these values back into the map.
14
+ * @param mapProps the props to update the map-instance with
7
15
* @internal
8
16
*/
9
- export function useMapOptions ( map : google . maps . Map | null , mapProps : MapProps ) {
10
- const { center, zoom, heading, tilt, ...mapOptions } = mapProps ;
17
+ export function useMapOptions (
18
+ map : google . maps . Map | null ,
19
+ cameraStateRef : InternalCameraStateRef ,
20
+ mapProps : MapProps
21
+ ) {
22
+ const { center : rawCenter , zoom, heading, tilt, ...mapOptions } = mapProps ;
23
+ const center = rawCenter
24
+ ? isLatLngLiteral ( rawCenter )
25
+ ? rawCenter
26
+ : rawCenter . toJSON ( )
27
+ : null ;
28
+ const lat = center && center . lat ;
29
+ const lng = center && center . lng ;
11
30
12
31
/* eslint-disable react-hooks/exhaustive-deps --
13
32
*
@@ -17,32 +36,48 @@ export function useMapOptions(map: google.maps.Map | null, mapProps: MapProps) {
17
36
*/
18
37
19
38
// update the map options when mapOptions is changed
39
+ // Note: due to the destructuring above, mapOptions will be seen as changed
40
+ // with every re-render, so we're boldly assuming the maps-api will properly
41
+ // deal with unchanged option-values passed into setOptions.
20
42
useEffect ( ( ) => {
21
43
if ( ! map ) return ;
22
44
23
- map . setOptions ( mapOptions ) ;
45
+ // Changing the mapId via setOptions will trigger an error-message.
46
+ // We will re-create the map-instance in that case anyway, so we
47
+ // remove it here to avoid this error-message.
48
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
+ const { mapId, ...opts } = mapOptions ;
50
+ map . setOptions ( opts ) ;
24
51
} , [ mapOptions ] ) ;
25
52
26
53
useLayoutEffect ( ( ) => {
27
- if ( ! map || ! center ) return ;
54
+ if ( ! map || ! Number . isFinite ( lat ) || ! Number . isFinite ( lng ) ) return ;
55
+ if (
56
+ cameraStateRef . current . center . lat === lat &&
57
+ cameraStateRef . current . center . lng === lng
58
+ )
59
+ return ;
28
60
29
- map . moveCamera ( { center} ) ;
30
- } , [ center ] ) ;
61
+ map . moveCamera ( { center : { lat : lat as number , lng : lng as number } } ) ;
62
+ } , [ lat , lng ] ) ;
31
63
32
64
useLayoutEffect ( ( ) => {
33
65
if ( ! map || ! Number . isFinite ( zoom ) ) return ;
66
+ if ( cameraStateRef . current . zoom === zoom ) return ;
34
67
35
68
map . moveCamera ( { zoom : zoom as number } ) ;
36
69
} , [ zoom ] ) ;
37
70
38
71
useLayoutEffect ( ( ) => {
39
72
if ( ! map || ! Number . isFinite ( heading ) ) return ;
73
+ if ( cameraStateRef . current . heading === heading ) return ;
40
74
41
75
map . moveCamera ( { heading : heading as number } ) ;
42
76
} , [ heading ] ) ;
43
77
44
78
useLayoutEffect ( ( ) => {
45
79
if ( ! map || ! Number . isFinite ( tilt ) ) return ;
80
+ if ( cameraStateRef . current . tilt === tilt ) return ;
46
81
47
82
map . moveCamera ( { tilt : tilt as number } ) ;
48
83
} , [ tilt ] ) ;
0 commit comments