Reputation: 1593
How can I adjust the code below so that map resizes when I resize the browser/screen? (First time using Mapbox without a senior developers help).
From my own research, there are code libraries that can add an event listener on the map container and listen for that to resize, then I can then call map.resize() programmatically. However, I would like to know what best practice are as that way feels a bit hacky.
Code
const applyToArray = (func, array) => func.apply(Math, array)
const getBoundsForPoints = (points) => {
console.log('Points:', points)
// Calculate corner values of bounds
const pointsLong = points.map(point => point.geometry.coordinates[0])
const pointsLat = points.map(point => point.geometry.coordinates[1])
const cornersLongLat = [
[applyToArray(Math.min, pointsLong), applyToArray(Math.min, pointsLat)],
[applyToArray(Math.max, pointsLong), applyToArray(Math.max, pointsLat)]
]
// Use WebMercatorViewport to get center longitude/latitude and zoom
const viewport = new WebMercatorViewport({ width: 600, height: 600 })
// @ts-ignore
.fitBounds(cornersLongLat, { padding: {top:20, bottom: 90, left:20, right:20} })
const { longitude, latitude, zoom } = viewport
return { longitude, latitude, zoom }
}
const myMap = () => {
const bounds = getBoundsForPoints(parkData.features);
const [viewport, setViewport] = useState({
width: "100%",
height: "600px",
...bounds
});
const [selectedPark, setSelectedPark] = useState(null);
return (
<div>
<ReactMapGL
{...viewport}
mapboxApiAccessToken="pk.eyJ1IjoiYmVubmtpbmd5IiwiYSI6ImNrY2ozMnJ5dzBrZ28ycnA1b2Vqb2I0bXgifQ.ZOaVtzsDQOrAovh9Orh13Q"
mapStyle="mapbox://styles/mapbox/streets-v11"
onViewportChange={viewport => {
setViewport(viewport);
}}
>
{parkData.features.map(park => (
<Marker
key={park.properties.ID}
latitude={park.geometry.coordinates[1]}
longitude={park.geometry.coordinates[0]}
>
<button
className="marker-btn"
onClick={e => {
e.preventDefault();
setSelectedPark(park);
}}
>
<img src={mapIcon} alt="Map Pointer Icon" />
</button>
</Marker>
))}
</ReactMapGL>
{selectedPark ? (
<div className={ styles.officeInfo }>
<div className={ styles.officeInfoImage }>
<img src={selectedPark.properties.IMAGE} />
</div>
<div className={ styles.officeInfoContent }>
<h3>{selectedPark.properties.NAME}</h3>
<p>{selectedPark.properties.DESCRIPTION}</p>
<button onClick={e => {
e.preventDefault();
setSelectedPark(null);
}}>X</button>
</div>
</div>
) : null}
</div>
);
}
Code demo https://codesandbox.io/s/immutable-glitter-hokq6?file=/src/App.js
I will legit buy you a coffee or two for helping <3
Upvotes: 5
Views: 4937
Reputation: 1625
Having worked with mapbox pretty extensively, I can tell you that map.resize()
will have to be called at some point, either by you, or by the react-map-gl bindings internally.
So, you need to first create a resize-handler, I prefer to do this in a hook:
const useResize = (handler) => {
useEffect(() => {
window.addEventListener("resize", handler);
return () => {
window.removeEventListener("resize", handler);
};
}, [handler]);
};
Then you can use this in your component as such:
const onResize = useCallback(() => {
setViewport({ ...viewport });
}, []);
useResize(onResize);
Since you update the viewport object, react-map-gl will recognize this as a state-change and update the viewport accordingly, probably by calling map.resize()
internally.
Here is a sandbox: https://codesandbox.io/s/upbeat-tdd-o8vn3?file=/src/App.js
The other (arguably better) solution is to simply call map.resize()
yourself. To do this, simply capture the map
object on load:
<ReactMapGL
{...viewport}
onLoad={({ map }) => setMap(map)}
>
Then call map.resize()
in your resize-handler:
const onResize = useCallback(() => {
if (map) {
map.resize();
}
}, [map]);
This solution is going to have better performance, since it is closer to the metal and doesn't require cloning the current viewport object.
Here's a sandbox for this solution: https://codesandbox.io/s/optimistic-kalam-8p8jo?file=/src/App.js
Upvotes: 4