I'm fairly new to the useMemo
and useCallback
hooks within react and I'm kind of stuck as to why my functions still continue to re-render even if the variable I'm checking against stays the same. Currently I'm trying to build a google map that allows the user to turn on and off certain markers that are displayed on the map. My functional component should only have to render these markers once on load and then memoize them after that since they are just being turned on/off afterwards. Now I understand the concept that hooks such as these only do a shallow comparison, but I'm comparing boolean values which I would think these hooks would be able to understand.
This is my current map file:
// constants are above
function Map() {
const {isLoaded} = useJsApiLoader({
googleMapsApiKey: env.GOOGLE_MAPS_API.API_KEY,
libraries: libs
})
const [map, setMap] = useState(null);
const [places,setPlaces] = useState([]);
const [places2,setPlaces2] = useState([]);
const [heatMap,setHeatMap] = useState([]);
const [fetchingData, setFetchingData] = useState(true);
const [showPlaces,setShowPlaces] = useState(true);
const [showPlaces2,setShowPlaces2] = useState(false);
const [showHeatMap,setShowHeatMap] = useState(false);
useEffect(() => {
let mounted = true;
if(mounted){
socket.emit('getMapAnalytics', ([heatmap,places,places2]) => {
console.log({heatmap})
setPlaces(places);
setPlaces2(places2);
setHeatMap(heatmap);
setFetchingData(false);
});
}
return () => {
mounted = false;
}
},[])
const onLoad = useCallback((map) => {
setMap(map);
},[])
const onUnmount = useCallback((map) => {
setMap(null);
},[]);
const renderPlacesMarkers = useCallback(() => {
console.log('rendering places')
if(showPlaces){
return places.map(place => (
<Marker icon={icon} key={place.ID} title={place.Name} position={{lat:place.Latitude,lng:place.Longitude}}/>
))
}
return null;
},[showPlaces]);
const renderPlaces2Markers = useCallback(() => {
console.log('rendering places2')
if(showPlaces2){
return places2.map(place => (
<Marker key={place.ID} title={place.ID} position={{lat:place.Latitude,lng:place.Longitude}}/>
))
}
return null;
},[showPlaces2]);
const renderHeatMap = useCallback(() => {
console.log('rendering heat map')
if(showHeatMap){
const mapData = heatMap.map(location => ({
location: new google.maps.LatLng(location.Latitude,location.Longitude), weight: location.population_density
}))
return (<HeatmapLayer data={mapData} options={{gradient: gradient}} />)
}
return null;
},[showHeatMap]);
const onChecked = (e) => {
switch(e.target.id){
case 'places': setShowPlaces(e.target.checked);
break;
case 'places2': setShowPlaces2(e.target.checked);
break;
case 'heat-map': setShowHeatMap(e.target.checked);
break;
}
}
return isLoaded && !fetchingData ? (
<div id="map-container">
<div id="top-bar" style={{display: 'flex', justifyContent:'space-evenly'}}>
<div style={{display: 'flex'}}>
<input type="checkbox" id="places" checked={showPlaces} onChange={onChecked}></input><label>techs</label>
</div>
<div style={{display: 'flex'}}>
<input type="checkbox" id="places2" checked={showPlaces2} onChange={onChecked}></input><label>kiosks</label>
</div>
<div style={{display: 'flex'}}>
<input type="checkbox" id="heat-map" checked={showHeatMap} onChange={onChecked}></input><label>heatmap</label>
</div>
</div>
<GoogleMap mapContainerStyle={containerStyle} center={center} zoom={10} onLoad={onLoad} onUnmount={onUnmount}>
{renderPlacesMarkers()}
{renderPlaces2Markers()}
{renderHeatMap()}
</GoogleMap>
</div>
) : <Spinner margin="margin-25"></Spinner>
}
export default memo(Map);
I've tried setting the useCallback
parameter to something else such as the array length of places and places2 but that still ended up re-rendering each function still.
The only thing that I could think of that is making these methods re-render each time is because I'm returning an array of JSX elements, but I could not find good examples of this online. If anyone could point me in the right direction to rightfully understand why this is happening, that would be great. TIA!