mirror of
https://github.com/visgl/react-map-gl.git
synced 2026-01-18 15:54:22 +00:00
[chore] Improve documentations; fix lint warnings (#1732)
This commit is contained in:
parent
f666ef87d2
commit
9164e321fb
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
175
docs/get-started/tips-and-tricks.md
Normal file
175
docs/get-started/tips-and-tricks.md
Normal 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>
|
||||
);
|
||||
```
|
||||
@ -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" }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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});
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -17,6 +17,7 @@ export function transformToViewState(tr: Transform): ViewState {
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-disable complexity */
|
||||
/**
|
||||
* Mutate a transform to match the given view state
|
||||
* @param transform
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user