[chore] Improve documentations; fix lint warnings (#1732)

This commit is contained in:
Xiaoji Chen 2022-02-07 16:51:32 -08:00 committed by Xiaoji Chen
parent f666ef87d2
commit 9164e321fb
14 changed files with 196 additions and 7 deletions

View File

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

View File

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

View File

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

View File

@ -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>
);
```

View File

@ -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" }
]
},
{

View File

@ -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() {

View File

@ -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);
@ -132,7 +133,7 @@ export default function GeocoderControl(props: GeocoderControlProps) {
return marker;
}
function noop() {}
const noop = () => {};
GeocoderControl.defaultProps = {
onLoading: noop,

View File

@ -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 => {

View File

@ -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(() => {

View File

@ -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});

View File

@ -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(() => {

View File

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

View File

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

View File

@ -17,6 +17,7 @@ export function transformToViewState(tr: Transform): ViewState {
};
}
/* eslint-disable complexity */
/**
* Mutate a transform to match the given view state
* @param transform