Reputation: 1411
I have started learning react hooks and I want to load map on initial render with cooridnates which I get from the backend, but somehow my map is getting rendered before my api give the data back, how do I make sure my map will wait till data is return ? do I need to put any condition on that?
below is api code
import React, { useState, useEffect, useRef } from "react";
import { useDispatch, useSelector } from 'react-redux';
import mapboxgl from 'mapbox-gl';
import { getBikeInfo, mapDetails } from './features/counter/getInfo/getDetails.js'
function App() {
const dispatch = useDispatch();
const dataToDisplay = useSelector(mapDetails);
const mapContainer = useRef(null);
mapboxgl.accessToken = 'pk.eyJ1IjoidmloYW5nMTYiLCJhIjoiY2ttOHowc2ZhMWN2OTJvcXJ0dGpiY21pNyJ9.hK5Wxwby89E7tKWoBoY5bg';
useEffect(() => {
dispatch(getBikeInfo())
var map = new mapboxgl.Map({
container: mapContainer.current,
style: 'mapbox://styles/mapbox/light-v10',
center: [-96, 37.8],
zoom: 3
});
console.log('mapdetails:' + mapDetails)
console.log('data display:' + dataToDisplay)
map.on('load', function () {
// Add an image to use as a custom marker
map.loadImage(
'https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png',
function (error, image) {
if (error) throw error;
map.addImage('custom-marker', image);
// Add a GeoJSON source with 2 points
map.addSource('points', {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': dataToDisplay
}
});
// Add a symbol layer
map.addLayer({
'id': 'points',
'type': 'symbol',
'source': 'points',
'layout': {
'icon-image': 'custom-marker',
// get the title name from the source's "title" property
'text-field': ['get', 'title'],
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [0, 1.25],
'text-anchor': 'top'
}
});
}
);
});
}, []);
return (
<div className="district-map-wrapper">
<div id="districtDetailMap" className="map">
<div style={{ height: "100%" }} ref={mapContainer}>
</div>
</div>
</div>
);
}
export default App;
below is my updated code:
useEffect(() => {
if (dataToDisplay.length != 0) {
loadtheMap
}
}, []);
but after some refresh I see data is not getting populated and setting dataDisplay
to []
.
update 1: based on suggestion I have updated my code like this
if(!dataLoaded) { // do not render anything if you're not ready
return (
<div className="hidden">
<div id="districtDetailMap" className="map">
<div style={{ height: "100%" }} ref={mapContainer}>
</div>
</div>
</div>);
}
where className="hidden" is define in App.css like below
.hidden{
display: none;
}
but still I am on same issue
as requested PFB my sandbox link
Upvotes: 1
Views: 2769
Reputation: 298
Since you're using useEffect
you are basically guaranteed that your component will render once before it even initiates the query
If you want to ensure your component won't be rendered, use some flag to indicate that data is ready, set it to true
after you set up your Map and conditionally render some fallback ui while this flag is false
function App() {
const [dataLoaded, setDataLoaded] = useState(false); // introduce the flag
const dispatch = useDispatch();
const dataToDisplay = useSelector(mapDetails);
const mapContainer = useRef(null);
mapboxgl.accessToken = 'pk.eyJ1IjoidmloYW5nMTYiLCJhIjoiY2ttOHowc2ZhMWN2OTJvcXJ0dGpiY21pNyJ9.hK5Wxwby89E7tKWoBoY5bg';
useEffect(() => {
dispatch(getBikeInfo())
var map = new mapboxgl.Map({
container: mapContainer.current,
style: 'mapbox://styles/mapbox/light-v10',
center: [-96, 37.8],
zoom: 3
});
console.log('mapdetails:' + mapDetails)
console.log('data display:' + dataToDisplay)
map.on('load', function () {
// Add an image to use as a custom marker
map.loadImage(
'https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png',
function (error, image) {
if (error) throw error;
map.addImage('custom-marker', image);
// Add a GeoJSON source with 2 points
map.addSource('points', {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': dataToDisplay
}
});
// Add a symbol layer
map.addLayer({
'id': 'points',
'type': 'symbol',
'source': 'points',
'layout': {
'icon-image': 'custom-marker',
// get the title name from the source's "title" property
'text-field': ['get', 'title'],
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [0, 1.25],
'text-anchor': 'top'
}
});
setDataLoaded(true); // set it to true when you're done
}
);
});
}, []);
return (
<div className="district-map-wrapper" style={dataLoaded ? undefined : {display: 'none'}}>
<div id="districtDetailMap" className="map">
<div style={{ height: "100%" }} ref={mapContainer}>
</div>
</div>
</div>
);
}
Upvotes: 1