Skip to content

Commit

Permalink
[chore] Improve documentations; fix lint warnings (#1732)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress committed Feb 8, 2022
1 parent f666ef8 commit 9164e32
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 7 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ There are several ways to provide a token to your app, as showcased in some of t
* Set the `MapboxAccessToken` environment variable (or set `REACT_APP_MAPBOX_ACCESS_TOKEN` if you are using Create React App)
* Provide it in the URL, e.g `?access_token=TOKEN`

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.


### Contribute

Expand Down
5 changes: 5 additions & 0 deletions docs/get-started/adding-custom-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ For details about data sources and layer configuration, check out the [Mapbox st
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.


## Custom Overlays

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).


## Other vis.gl Libraries

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.
Expand Down
4 changes: 2 additions & 2 deletions docs/get-started/mapbox-tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ To get a Mapbox token, you will need to register on [their website](https://www.

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

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

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.
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.

## Display Maps Without A Mapbox Token

Expand Down
175 changes: 175 additions & 0 deletions docs/get-started/tips-and-tricks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Tips and Tricks

## Securing Mapbox Token

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.

- Never commit your token in clear text into GitHub or other source control.
- 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.
- 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.
- Add the following to your bundler config:

```js
/// webpack.config.js
const {DefinePlugin} = require('webpack');

module.exports = {
...
plugins: [
new DefinePlugin({
'process.env.MapboxAccessToken': JSON.stringify(process.env.NODE_ENV == 'production' ? process.env.MapboxAccessTokenProd : process.env.MapboxAccessTokenDev)
})
]
};
```

```js
/// rollup.config.js
const replace = require('@rollup/plugin-replace').default;

module.exports = {
...
plugins: [
replace({
'process.env.MapboxAccessToken': JSON.stringify(process.env.NODE_ENV == 'production' ? process.env.MapboxAccessTokenProd : process.env.MapboxAccessTokenDev)
})
]
};
```

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__}`.


## Minimize Cost from Frequent Re-mounting

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:

```jsx
/// Example using Tabs from Material UI
<TabContext value={selectedTab}>
<TabList onChange={handleTabChange}>
<Tab label="Map" value="map" />
<Tab label="List" value="table" />
</TabList>
<TabPanel value="map">
<Map
mapStyle="mapbox://styles/mapbox/streets-v9" >
{items.map(renderMarker)}
</Map>
</TabPanel>
<TabPanel value="table">
<Table>
{items.map(renderRow)}
</Table>
</TabPanel>
</TabContext>
```

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.

In this case, it is recommended that you set the [reuseMaps](/docs/api-reference/map.md#reuseMaps) prop to `true`:

```jsx
<TabPanel value="map">
<Map reuseMaps
mapStyle="mapbox://styles/mapbox/streets-v9" >
{items.map(renderMarker)}
</Map>
</TabPanel>
```

This bypasses the initialization when a map is removed then added back.

## Performance with Many Markers

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:

```jsx
import {useSelector, useDispatch} from 'react-redux';
import Map, {Marker} from 'react-map-gl';

function MapView() {
const viewState = useSelector(s => s.viewState);
const vehicles = useSelector(s => s.vehicles);
const dispatch = useDispatch();

const onMove = useCallback(evt => {
dispatch({type: 'setViewState', payload: evt.viewState});
}, []);

return (
<Map
{...viewState}
onMove={onMove}
mapStyle="mapbox://styles/mapbox/streets-v9" >
>
{vehicles.map(vehicle => (
<Marker key={vehicle.id}
longitude={vehicle.coordinates[0]}
latitude={vehicle.coordinates[1]}>
<svg>
// vehicle icon
</svg>
</Marker>)
)}
</Map>
);
}
```

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.

One way to improve the performance is `useMemo`:

```jsx
const markers = useMemo(() => vehicles.map(vehicle => (
<Marker key={vehicle.id}
longitude={vehicle.coordinates[0]}
latitude={vehicle.coordinates[1]}>
<svg>
// vehicle icon
</svg>
</Marker>)
)}, [vehicles]);

return (
<Map
{...viewState}
onMove={onMove}
mapStyle="mapbox://styles/mapbox/streets-v9" >
>
{markers}
</Map>
);
}
```

This prevents React from rerendering the markers unless they have changed.

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:

```jsx
const vehiclesGeoJSON = useMemo(() => {
return {
type: 'FeatureCollection',
features: vehicles.map(vehicle => turf.point(vehicle.coordinates, vehicle))
};
}, [vehicles]);

return (
<Map
{...viewState}
onMove={onMove}
mapStyle="mapbox://styles/mapbox/streets-v9" >
>
<Source id="vehicles" type="geojson" data={vehiclesGeoJSON}>
<Layer type="symbol"
layout={{
'icon-image': 'vehicle-icon',
'icon-size': 1,
'text-field': ['get', 'id']
}} >
</Sources>
</Map>
);
```
1 change: 1 addition & 0 deletions docs/table-of-contents.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{ "entry": "docs/get-started/mapbox-tokens" },
{ "entry": "docs/get-started/state-management" },
{ "entry": "docs/get-started/adding-custom-data" }
{ "entry": "docs/get-started/tips-and-tricks" }
]
},
{
Expand Down
1 change: 1 addition & 0 deletions examples/geocoder/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Map from 'react-map-gl';
import GeocoderControl from './geocoder-control';
import ControlPanel from './control-panel';

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

export default function App() {
Expand Down
3 changes: 2 additions & 1 deletion examples/geocoder/src/geocoder-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type GeocoderControlProps = {
onError?: (e: object) => void;
};

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

Expand Down Expand Up @@ -132,7 +133,7 @@ export default function GeocoderControl(props: GeocoderControlProps) {
return marker;
}

function noop() {}
const noop = () => {};

GeocoderControl.defaultProps = {
onLoading: noop,
Expand Down
3 changes: 2 additions & 1 deletion examples/geojson/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export default function App() {
'https://raw.githubusercontent.com/uber/react-map-gl/master/examples/.data/us-income.geojson'
)
.then(resp => resp.json())
.then(json => setAllData(json));
.then(json => setAllData(json))
.catch(err => console.error('Could not load data', err)); // eslint-disable-line
}, []);

const onHover = useCallback(event => {
Expand Down
3 changes: 2 additions & 1 deletion examples/heatmap/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export default function App() {
setTimeRange([startTime, endTime]);
setEarthQuakes(json);
selectTime(endTime);
});
})
.catch(err => console.error('Could not load data', err)); // eslint-disable-line
}, []);

const data = useMemo(() => {
Expand Down
1 change: 1 addition & 0 deletions src/components/marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const defaultProps: Partial<MarkerProps> = {
pitchAlignment: 'auto'
};

/* eslint-disable complexity,max-statements */
function Marker(props: MarkerProps) {
const {map, mapLib} = useContext(MapContext);
const thisRef = useRef({props});
Expand Down
1 change: 1 addition & 0 deletions src/components/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function getClassList(className: string) {
return new Set(className ? className.trim().split(/\s+/) : []);
}

/* eslint-disable complexity,max-statements */
function Popup(props: PopupProps) {
const {map, mapLib} = useContext(MapContext);
const container = useMemo(() => {
Expand Down
2 changes: 2 additions & 0 deletions src/mapbox/mapbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ export default class Mapbox {
return that;
}

/* eslint-disable complexity,max-statements */
_initialize(container: HTMLDivElement) {
const {props} = this;
const mapOptions = {
Expand Down Expand Up @@ -575,6 +576,7 @@ export default class Mapbox {
}
this._map = map;
}
/* eslint-enable complexity,max-statements */

recycle() {
Mapbox.savedMaps.push(this);
Expand Down
1 change: 1 addition & 0 deletions src/utils/deep-equal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export function arePointsEqual(a?: PointLike, b?: PointLike): boolean {
return ax === bx && ay === by;
}

/* eslint-disable complexity */
/**
* Compare any two objects
* @param a
Expand Down
1 change: 1 addition & 0 deletions src/utils/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function transformToViewState(tr: Transform): ViewState {
};
}

/* eslint-disable complexity */
/**
* Mutate a transform to match the given view state
* @param transform
Expand Down

0 comments on commit 9164e32

Please sign in to comment.