-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into release-please--branches--main--components--…
…@vis.gl/react-google-maps
- Loading branch information
Showing
5 changed files
with
156 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,68 @@ | ||
# Introduction | ||
|
||
react-google-maps is a collection of [React](https://react.dev/) components and hooks for | ||
the Google Maps Javascript API. | ||
`@vis.gl/react-google-maps` is a collection of [React][react] components and | ||
hooks for the Google Maps Javascript API. It was first provisioned by the | ||
Google Maps Platform team, in order to provide solid integrations between | ||
React and the Maps JavaScript API inspired by the integration between | ||
`react-map-gl` and the map renderers based on `mapbox-gl-js`. | ||
|
||
Want to contribute? See our [Developer Guide](./contributing.md) | ||
|
||
## Design Philosophy | ||
|
||
react-google-maps was first provisioned by the Google Maps Platform team, in order to provide solid integrations between | ||
react and the Google Maps API inspired by the integration between react-map-gl and the map renderers based on | ||
mapbox-gl-js. | ||
The Google Maps JavaScript API follows an [imperative][wiki-imperative] | ||
programming model. That is, you instruct the map to do something, and it | ||
will execute the command at its own pace. The map also maintains a lot of | ||
its own state-information and logic for updating it. For example, the virtual | ||
camera and the various ways to control it are fully contained within the | ||
Maps API, including all event-listeners that allow users to interact with | ||
the map. | ||
|
||
The Google Maps APIs are [imperative](https://en.wikipedia.org/wiki/Imperative_programming). | ||
That is, you instruct the map to do something, and it will execute the command at its own pace. | ||
The map also maintains a lot of its own state-information and logic for updating it, for example | ||
the current viewport and the various ways to control it is fully contained within the maps API. | ||
While this is typically what you'd expect from a map renderer, it also comes | ||
with some problems in the context of a React application. We often need a | ||
full synchronization of the map-state with the state of the application and | ||
other components. Some examples: | ||
|
||
While this may be fine in a lot of use-cases it does not work when many components need to synchronize | ||
with each other. We sometimes render two maps side by side, and when the user interacts with one, | ||
update both cameras. We draw React UI outside of the map container, that moves with the camera. | ||
We also render WebGL graphic overlays on top of the map, most notably with [deck.gl](https://deck.gl). | ||
- Two maps are rendered side by side, and user interactions in one of them | ||
should update both maps. | ||
- Other components and UI elements outside the map might need updating based on | ||
the region currently visible, or they might trigger changes of the region | ||
shown by the map. | ||
- Integrations with overlays for data-visualization on top of the map, most | ||
notably using [deck.gl][docs-deckgl]. | ||
|
||
In these use cases, in order for all components to synchronize correctly, they must have their shared | ||
states managed by React. We might store the **source of truth** in a parent component state, or Redux | ||
store, or hooks, and let it propagate down to the map as well as its peers. | ||
In all of these cases, the application must have a shared state managed by | ||
React to be able to synchronize the components correctly. This is referred | ||
to as having a _single source of truth_. That could be stored in a parent | ||
component state, a Redux store, or react-hooks, and let it propagate down | ||
to the map and any other component. | ||
|
||
Ultimately, in the spirit of the [reactive programming paradigm](https://en.wikipedia.org/wiki/Reactive_programming), | ||
data always flows **down**. As long as the map manages its own state, as Google Maps is designed to do, we risk the | ||
components going out of sync. | ||
Ultimately, in the spirit of the [reactive programming paradigm] | ||
[wiki-reactive] used in React, data from this single source of truth should | ||
always flow down the component hierarchy. If components manage their own | ||
state, as Google Maps is designed to do, we risk the components going out of | ||
sync. | ||
|
||
@vis.gl/react-google-maps can create a fully reactive wrapper for the Google Maps JavaScript API. | ||
The [Map](./api-reference/components/map.md) component can be fully | ||
[controlled](https://reactjs.org/docs/forms.html#controlled-components), that is, the map's camera would | ||
never deviate from the props that have been assigned. | ||
`@vis.gl/react-google-maps` provides a reactive wrapper for the Google Maps | ||
JavaScript API while still leaving the map instance in charge of state | ||
updates. In most cases, this is desirable, as controlling a map from mouse, | ||
pointer and touch events is a complicated problem in itself which is solved | ||
particularly well by the Maps JavaScript API. | ||
|
||
This means that usually, the props specified for the camera are read as | ||
initial values, and users will be able to interact with the map without | ||
requiring the props to be updated. So by default, the map is allowed to | ||
deviate from the specified values. | ||
|
||
However, the [Map][docs-map] component can also be used as a fully [controlled] | ||
[react-controlled] component. That is, the map's camera will never deviate | ||
from the props that have been assigned. In this mode, the controls | ||
provided by the map can still be used by listening for the appropriate | ||
events and updating the state accordingly. | ||
|
||
[wiki-imperative]: https://en.wikipedia.org/wiki/Imperative_programming | ||
[wiki-reactive]: https://en.wikipedia.org/wiki/Reactive_programming | ||
[docs-deckgl]: ./guides/deckgl-integration.md | ||
[docs-map]: ./api-reference/components/map.md | ||
[react]: https://react.dev/ | ||
[react-controlled]: https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# DeckGL Integration | ||
|
||
## Using the `Map` as a child of `DeckGL` | ||
|
||
To use deck.gl with the `Map` component wrap the `Map` component with | ||
the [DeckGL React component](https://deck.gl/docs/get-started/using-with-react). | ||
|
||
The following props need to be defined and passed to the DeckGL component: | ||
|
||
- `intialViewState` | ||
- `layers` | ||
- `controller` | ||
- `onViewStateChange` | ||
|
||
Make sure that the controller is always set to true and an initial view state is defined. To ensure that deck.gl works | ||
well with the Google Maps Platform map, set the `limitTiltRange` function, which can be imported from the Google Maps React | ||
library, to `onViewStateChange`. | ||
|
||
```tsx | ||
import {limitTiltRange} from '@vis.gl/react-google-maps'; | ||
|
||
const App = () => ( | ||
<APIProvider apiKey={'Your API key here'}> | ||
<DeckGL | ||
initialViewState={INITIAL_VIEW_STATE} | ||
layers={layers} | ||
controller={true} | ||
onViewStateChange={limitTiltRange}> | ||
<Map {...GOOGLE_MAPS_MAP_OPTIONS} /> | ||
</DeckGL> | ||
</APIProvider> | ||
); | ||
``` | ||
|
||
## Using GoogleMapsOverlay | ||
|
||
Alternatively, you can also use the `GoogleMapsOverlay` provided by the `@deck.gl/google-maps` package to render deck.gl | ||
content via Maps API `WebGlOverlayView`. An example for this can be found in [`./examples/deckgl-overlay`](https://github.com/visgl/react-google-maps/tree/1a0ac6e13d15ceda5212d310ffc2370ffdd90e65/examples/deckgl-overlay). | ||
|
||
For this you have to implement your own component and add it to the `Map` component. | ||
A simplified version of this would be: | ||
|
||
```javascript | ||
import {useEffect, useMemo} from 'react'; | ||
import {useMap} from '@vis.gl/react-google-maps'; | ||
import {GoogleMapsOverlay} from '@deck.gl/google-maps'; | ||
|
||
export const DeckGlOverlay = ({layers}) => { | ||
const deck = useMemo(() => new GoogleMapsOverlay({interleaved: true}), []); | ||
|
||
const map = useMap(); | ||
useEffect(() => deck.setMap(map), [map]); | ||
useEffect(() => deck.setProps({layers}), [layers]); | ||
|
||
// no dom rendered by this component | ||
return null; | ||
}; | ||
|
||
const App = () => ( | ||
<APIProvider apiKey={API_KEY}> | ||
<Map {...mapProps}> | ||
<DeckGlOverlay layers={deckGlLayers} /> | ||
</Map> | ||
</APIProvider> | ||
); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters