Skip to content

Commit 9164e32

Browse files
committed
[chore] Improve documentations; fix lint warnings (#1732)
1 parent f666ef8 commit 9164e32

File tree

14 files changed

+196
-7
lines changed

14 files changed

+196
-7
lines changed

Diff for: README.md

-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ There are several ways to provide a token to your app, as showcased in some of t
5454
* Set the `MapboxAccessToken` environment variable (or set `REACT_APP_MAPBOX_ACCESS_TOKEN` if you are using Create React App)
5555
* Provide it in the URL, e.g `?access_token=TOKEN`
5656

57-
But we would recommend using something like [dotenv](https://github.com/motdotla/dotenv) and put your key in an untracked `.env` file, that will then expose it as a `process.env` variable, with much less leaking risks.
58-
5957

6058
### Contribute
6159

Diff for: docs/get-started/adding-custom-data.md

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ For details about data sources and layer configuration, check out the [Mapbox st
4545
For dynamically updating data sources and layers, check out the [GeoJSON](http://visgl.github.io/react-map-gl/examples/geojson) and [GeoJSON animation](http://visgl.github.io/react-map-gl/examples/geojson-animation) examples.
4646

4747

48+
## Custom Overlays
49+
50+
You can implement a custom HTML or SVG overlay on top of the map that redraws whenever the camera changes. By calling `map.project()` you can adjust the DOM or CSS properties so that the customly-drawn features are always aligned with the map. See a full example [here](https://github.com/visgl/react-map-gl/tree/7.0-release/examples/custom-overlay).
51+
52+
4853
## Other vis.gl Libraries
4954

5055
For more feature rich and performant data visualization overlay use cases, you may consider using other visualization libraries. react-map-gl is part of the [vis.gl](https://www.github.com/visgl) ecosystem, a suite of high-performance data visualization tools for the Web.

Diff for: docs/get-started/mapbox-tokens.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ To get a Mapbox token, you will need to register on [their website](https://www.
1010

1111
There are several ways to provide a token to your app, as showcased in some of the example folders:
1212

13-
* Provide a `mapboxApiAccessToken` prop to the map component
13+
* Provide a `mapboxAccessToken` prop to the map component
1414
* Set the `MapboxAccessToken` environment variable (or set `REACT_APP_MAPBOX_ACCESS_TOKEN` if you are using Create React App)
1515
* Provide it in the URL, e.g `?access_token=TOKEN`
1616

17-
But we would recommend using something like [dotenv](https://github.com/motdotla/dotenv) and put your key in an untracked `.env` file, that will then expose it as a `process.env` variable, with much less leaking risks.
17+
We recommend using an environment variable to minimize leaking risks. See [securing Mapbox token](/docs/get-started/tips-and-tricks.md#securing-mapbox-token) for examples.
1818

1919
## Display Maps Without A Mapbox Token
2020

Diff for: docs/get-started/tips-and-tricks.md

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Tips and Tricks
2+
3+
## Securing Mapbox Token
4+
5+
Because Mapbox tokens are required for the client application to make requests to Mapbox servers, you have to distribute it with your app. It is not possible to stop a visitor to your site from scraping the token. The practice outlined below can help you protect your token from being abused.
6+
7+
- Never commit your token in clear text into GitHub or other source control.
8+
- In your local dev environment, define the token in an environment variable e.g. `MapboxAccessTokenDev=...` in the command line, or use something like [dotenv](https://github.com/motdotla/dotenv) and put `MapboxAccessTokenDev=...` in a `.env` file. Add `.env` to `.gitignore` so it's never tracked. If your app is deployed by a continuous integration pipeline, follow its documentation and set a secret environment variable.
9+
- Create separate tokens for development (often times on `http://localhost`), public code snippet (Gist, Codepen etc.) and production (deployed to `https://mycompany.com`). The public token should be rotated regularly. The production token should have strict [scope and URL restrictions](https://docs.mapbox.com/help/troubleshooting/how-to-use-mapbox-securely/#access-tokens) that only allows it to be used on a domain that you own.
10+
- Add the following to your bundler config:
11+
12+
```js
13+
/// webpack.config.js
14+
const {DefinePlugin} = require('webpack');
15+
16+
module.exports = {
17+
...
18+
plugins: [
19+
new DefinePlugin({
20+
'process.env.MapboxAccessToken': JSON.stringify(process.env.NODE_ENV == 'production' ? process.env.MapboxAccessTokenProd : process.env.MapboxAccessTokenDev)
21+
})
22+
]
23+
};
24+
```
25+
26+
```js
27+
/// rollup.config.js
28+
const replace = require('@rollup/plugin-replace').default;
29+
30+
module.exports = {
31+
...
32+
plugins: [
33+
replace({
34+
'process.env.MapboxAccessToken': JSON.stringify(process.env.NODE_ENV == 'production' ? process.env.MapboxAccessTokenProd : process.env.MapboxAccessTokenDev)
35+
})
36+
]
37+
};
38+
```
39+
40+
react-map-gl automatically picks up `process.env.MapboxAccessToken` or `process.env.REACT_APP_MAPBOX_ACCESS_TOKEN` if they are defined. Alternatively, you can use your own variable name (e.g. `__SUPER_SECRET_TOKEN__`) and pass it in manually with `mapboxAccessToken={__SUPER_SECRET_TOKEN__}`.
41+
42+
43+
## Minimize Cost from Frequent Re-mounting
44+
45+
In a moderately complex single-page app, as the user navigates through the UI, a map component may unmount and mount again many times during a session. Consider the following layout:
46+
47+
```jsx
48+
/// Example using Tabs from Material UI
49+
<TabContext value={selectedTab}>
50+
<TabList onChange={handleTabChange}>
51+
<Tab label="Map" value="map" />
52+
<Tab label="List" value="table" />
53+
</TabList>
54+
<TabPanel value="map">
55+
<Map
56+
mapStyle="mapbox://styles/mapbox/streets-v9" >
57+
{items.map(renderMarker)}
58+
</Map>
59+
</TabPanel>
60+
<TabPanel value="table">
61+
<Table>
62+
{items.map(renderRow)}
63+
</Table>
64+
</TabPanel>
65+
</TabContext>
66+
```
67+
68+
Every time the user clicks the "table" tab, the map is unmounted. When they click the "map" tab, the map is mounted again. As of v2.0, mapbox-gl generates a [billable event](https://www.mapbox.com/pricing#maploads) every time a Map object is initialized. It is obviously not idea to get billed for just collapsing and expanding part of the UI.
69+
70+
In this case, it is recommended that you set the [reuseMaps](/docs/api-reference/map.md#reuseMaps) prop to `true`:
71+
72+
```jsx
73+
<TabPanel value="map">
74+
<Map reuseMaps
75+
mapStyle="mapbox://styles/mapbox/streets-v9" >
76+
{items.map(renderMarker)}
77+
</Map>
78+
</TabPanel>
79+
```
80+
81+
This bypasses the initialization when a map is removed then added back.
82+
83+
## Performance with Many Markers
84+
85+
If your application uses externally managed camera state, like with Redux, the number of React rerenders may be very high when the user is interacting with the map. Consider the following setup:
86+
87+
```jsx
88+
import {useSelector, useDispatch} from 'react-redux';
89+
import Map, {Marker} from 'react-map-gl';
90+
91+
function MapView() {
92+
const viewState = useSelector(s => s.viewState);
93+
const vehicles = useSelector(s => s.vehicles);
94+
const dispatch = useDispatch();
95+
96+
const onMove = useCallback(evt => {
97+
dispatch({type: 'setViewState', payload: evt.viewState});
98+
}, []);
99+
100+
return (
101+
<Map
102+
{...viewState}
103+
onMove={onMove}
104+
mapStyle="mapbox://styles/mapbox/streets-v9" >
105+
>
106+
{vehicles.map(vehicle => (
107+
<Marker key={vehicle.id}
108+
longitude={vehicle.coordinates[0]}
109+
latitude={vehicle.coordinates[1]}>
110+
<svg>
111+
// vehicle icon
112+
</svg>
113+
</Marker>)
114+
)}
115+
</Map>
116+
);
117+
}
118+
```
119+
120+
This component is rerendered on every animation frame when the user is dragging the map. If it's trying to render hundreds of markers, the performance lag will become quite visible.
121+
122+
One way to improve the performance is `useMemo`:
123+
124+
```jsx
125+
const markers = useMemo(() => vehicles.map(vehicle => (
126+
<Marker key={vehicle.id}
127+
longitude={vehicle.coordinates[0]}
128+
latitude={vehicle.coordinates[1]}>
129+
<svg>
130+
// vehicle icon
131+
</svg>
132+
</Marker>)
133+
)}, [vehicles]);
134+
135+
return (
136+
<Map
137+
{...viewState}
138+
onMove={onMove}
139+
mapStyle="mapbox://styles/mapbox/streets-v9" >
140+
>
141+
{markers}
142+
</Map>
143+
);
144+
}
145+
```
146+
147+
This prevents React from rerendering the markers unless they have changed.
148+
149+
If your application can do without complicated DOM objects and CSS styling, consider switching to a [symbol layer](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#symbol). Layers are rendered in WebGL and are much more performant than markers:
150+
151+
```jsx
152+
const vehiclesGeoJSON = useMemo(() => {
153+
return {
154+
type: 'FeatureCollection',
155+
features: vehicles.map(vehicle => turf.point(vehicle.coordinates, vehicle))
156+
};
157+
}, [vehicles]);
158+
159+
return (
160+
<Map
161+
{...viewState}
162+
onMove={onMove}
163+
mapStyle="mapbox://styles/mapbox/streets-v9" >
164+
>
165+
<Source id="vehicles" type="geojson" data={vehiclesGeoJSON}>
166+
<Layer type="symbol"
167+
layout={{
168+
'icon-image': 'vehicle-icon',
169+
'icon-size': 1,
170+
'text-field': ['get', 'id']
171+
}} >
172+
</Sources>
173+
</Map>
174+
);
175+
```

Diff for: docs/table-of-contents.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
{ "entry": "docs/get-started/mapbox-tokens" },
1818
{ "entry": "docs/get-started/state-management" },
1919
{ "entry": "docs/get-started/adding-custom-data" }
20+
{ "entry": "docs/get-started/tips-and-tricks" }
2021
]
2122
},
2223
{

Diff for: examples/geocoder/src/app.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Map from 'react-map-gl';
55
import GeocoderControl from './geocoder-control';
66
import ControlPanel from './control-panel';
77

8+
// eslint-disable-next-line
89
const TOKEN = process.env.MapboxAccessToken; // Set your mapbox token here
910

1011
export default function App() {

Diff for: examples/geocoder/src/geocoder-control.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type GeocoderControlProps = {
4747
onError?: (e: object) => void;
4848
};
4949

50+
/* eslint-disable complexity,max-statements */
5051
export default function GeocoderControl(props: GeocoderControlProps) {
5152
const [marker, setMarker] = useState(null);
5253

@@ -132,7 +133,7 @@ export default function GeocoderControl(props: GeocoderControlProps) {
132133
return marker;
133134
}
134135

135-
function noop() {}
136+
const noop = () => {};
136137

137138
GeocoderControl.defaultProps = {
138139
onLoading: noop,

Diff for: examples/geojson/src/app.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export default function App() {
2020
'https://raw.githubusercontent.com/uber/react-map-gl/master/examples/.data/us-income.geojson'
2121
)
2222
.then(resp => resp.json())
23-
.then(json => setAllData(json));
23+
.then(json => setAllData(json))
24+
.catch(err => console.error('Could not load data', err)); // eslint-disable-line
2425
}, []);
2526

2627
const onHover = useCallback(event => {

Diff for: examples/heatmap/src/app.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ export default function App() {
4343
setTimeRange([startTime, endTime]);
4444
setEarthQuakes(json);
4545
selectTime(endTime);
46-
});
46+
})
47+
.catch(err => console.error('Could not load data', err)); // eslint-disable-line
4748
}, []);
4849

4950
const data = useMemo(() => {

Diff for: src/components/marker.ts

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const defaultProps: Partial<MarkerProps> = {
7373
pitchAlignment: 'auto'
7474
};
7575

76+
/* eslint-disable complexity,max-statements */
7677
function Marker(props: MarkerProps) {
7778
const {map, mapLib} = useContext(MapContext);
7879
const thisRef = useRef({props});

Diff for: src/components/popup.ts

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ function getClassList(className: string) {
6969
return new Set(className ? className.trim().split(/\s+/) : []);
7070
}
7171

72+
/* eslint-disable complexity,max-statements */
7273
function Popup(props: PopupProps) {
7374
const {map, mapLib} = useContext(MapContext);
7475
const container = useMemo(() => {

Diff for: src/mapbox/mapbox.ts

+2
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ export default class Mapbox {
502502
return that;
503503
}
504504

505+
/* eslint-disable complexity,max-statements */
505506
_initialize(container: HTMLDivElement) {
506507
const {props} = this;
507508
const mapOptions = {
@@ -575,6 +576,7 @@ export default class Mapbox {
575576
}
576577
this._map = map;
577578
}
579+
/* eslint-enable complexity,max-statements */
578580

579581
recycle() {
580582
Mapbox.savedMaps.push(this);

Diff for: src/utils/deep-equal.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export function arePointsEqual(a?: PointLike, b?: PointLike): boolean {
1414
return ax === bx && ay === by;
1515
}
1616

17+
/* eslint-disable complexity */
1718
/**
1819
* Compare any two objects
1920
* @param a

Diff for: src/utils/transform.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export function transformToViewState(tr: Transform): ViewState {
1717
};
1818
}
1919

20+
/* eslint-disable complexity */
2021
/**
2122
* Mutate a transform to match the given view state
2223
* @param transform

0 commit comments

Comments
 (0)