google-maps-react-hooks
google-maps-react-hooks copied to clipboard
Issues with markers on Google Maps API - not always loading
I'm working on a website with Google Maps API, particularly with @ubilabs/google-maps-react-hooks. I'm not very experienced so I'm having some difficulties understanding why there's an issue. When I load the website (either locally or online) the markers don't always show up. It could be on loading the site at first or when I refresh, they disappear and reapper upon new refreshing of the page. This never happens if I have my developer tools open (docked or undocked) and I can't figure out why! I've been trying for days.
index.txs
import { GoogleMapsProvider } from '@ubilabs/google-maps-react-hooks';
import React, { useState, useEffect, useCallback} from 'react';
import { useGeoReportState } from '../../context/GeoReportContext';
import { useMapContext, useMapDispatch } from '../../context/MapContext';
import { options } from './MapElements';
import TestMap from './TestMap';
const Index = () => {
const [mapContainer, setMapContainer] = useState<HTMLDivElement | null>(null);
const mapDispatch = useMapDispatch();
const mapState = useMapContext();
const reportState = useGeoReportState();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onLoad = useCallback((map: any) => {
mapDispatch.setMap(map);
google.maps.event.trigger(map, 'resize');
}, [reportState]);
return (
<>
<div className='flex flex-col w-full h-full'>
<GoogleMapsProvider
mapOptions={options(mapState)}
googleMapsAPIKey={process.env.REACT_APP_API_KEY as string}
mapContainer={mapContainer}
version='beta'
onLoadMap={onLoad}
libraries={['places']}
>
<TestMap />
<div ref={(node) => setMapContainer(node)} style={{ height: '100vh' }} />
</GoogleMapsProvider>
</div>
</>
);
};
export default Index;
TestMap.tsx
import React, { useState, useEffect, SetStateAction } from 'react';
import { useGeoReportState } from '../../context/GeoReportContext';
import { useMapContext } from '../../context/MapContext';
import { findMode, formSubmitGeoCoder, getSoilTypeColor } from '../../utils/Util';
import Navbar from '../Navbar';
import { notificationHandler, surveyLengthHandler } from '../../utils/ContextHandlers';
import { MarkerClusterer, SuperClusterAlgorithm } from '@googlemaps/markerclusterer';
import Icon from '../../img/point.png';
import FileSubmit from '../Form/FileSubmit';
import Form from '../Form/Form';
import ThankYou from '../Form/ThankYou';
import { IconContext } from 'react-icons';
import { AiOutlineLeft, AiOutlineRight, AiOutlineReload } from 'react-icons/ai';
import { GeoReport, Survey } from '../../types/GeoReportTypes';
import { SurveyData } from '../../types/SurveyTypes';
import { useNotificationDispatch } from '../../context/NotificationContext';
import { useSurveyDispatch } from '../../context/SurveyContext';
import PointData from '../Form/PointData';
import { useGoogleMap } from '@ubilabs/google-maps-react-hooks';
const TestMap: React.FC = () => {
const geoReportState = useGeoReportState();
const notificationDispatch = useNotificationDispatch();
const mapState = useMapContext();
const surveyDispatch = useSurveyDispatch();
const map = useGoogleMap();
let formData = {};
const [showFileSubmit, setShowFileSubmit] = useState(false);
const [showSliderForms, showSlider] = useState(false);
const [showThankYouForms, setShowThankYou] = useState(false);
const [surveys, setSurveys] = useState<Survey[]>([]);
const [email, setEmail] = useState<string>();
const [thisMarkers, setThisMarkers] = useState<google.maps.Marker[]>([]);
const [circles, setCircles] = useState<google.maps.Circle[]>([]);
const [pointDataArray, setPointDataArray] = useState<GeoReport | null>(null);
const [distanceBetween, setDistanceBetween] = useState<number | null>(null);
const [surveyNumber, setSurveyNumber] = useState(surveys.length + 1);
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [selectedImage, setSelectedImage] =useState<string>();
const incrementCount = () => {
setSurveyNumber(surveyNumber + 1);
};
const resetCount = () => {
setSurveyNumber(1);
};
useEffect(() => {
if (!map) return;
if (thisMarkers.length == 0) {
Object.entries(geoReportState.map).map(([, reports]) => {
addMarkers(undefined, reports, thisMarkers, setThisMarkers, setPointDataArray);
addCircles(undefined, setPointDataArray, reports, circles, setCircles);
});
}
thisMarkers.forEach(marker => {
marker.setMap(map);
});
}, [mapState]);
useEffect(() => {
if (!map) return;
map.addListener('zoom_changed', () => {
if (map.getZoom()! >= 11) {
thisMarkers.forEach(marker => {
marker.setMap(null);
});
circles.forEach(circle => {
circle.setMap(map);
});
}
if (map.getZoom()! < 11) {
thisMarkers.forEach(marker => {
marker.setMap(map);
});
circles.forEach(circle => {
circle.setMap(null);
});
}
});
}, [map?.getZoom()]);
thisMarkers.forEach(marker => {
marker.addListener('click', () => {
if (showSliderForms === true) {
showSlider(false);
}
});
});
const handleOpenSlider = () => {
showSlider(!showSliderForms);
if (pointDataArray) {
handleClosePointData();
}
};
const handleOpenFileSubmit = () => {
if (surveys.length === 0) {
notificationHandler(2, 'Por favor introduza valores válidos', notificationDispatch);
} else {
setShowFileSubmit(!showFileSubmit);
};
};
const handleOpenThankYou = () => {
setShowThankYou(!showThankYouForms);
};
const handleClosePointData = () => {
setPointDataArray(null);
setDistanceBetween(null);
};
const handleSubmitNew = () => {
setShowThankYou(!showThankYouForms);
setShowFileSubmit(!showFileSubmit);
setSurveys([]);
resetCount();
setSelectedFile(null);
setSelectedImage('');
};
const handleExit = () => {
setShowThankYou(!showThankYouForms);
setShowFileSubmit(!showFileSubmit);
showSlider(!showSliderForms);
setSurveys([]);
resetCount();
setSelectedFile(null);
setSelectedImage('');
};
const handleAddSurvey = (event: { preventDefault: () => void; }, lat: number, lng: number, nFreatico: boolean, inFreatico: number, nSPT: boolean, table: SurveyData[]) => {
event.preventDefault();
if (lat === 0 || lng === 0 || lat === undefined || lng === undefined || table.length === 0 || (nFreatico === true && (inFreatico === undefined || inFreatico === 0))) {
notificationHandler(2, 'Por favor introduza valores válidos', notificationDispatch);
} else {
if(nSPT) {
table.map(e => e.spt1 = 1000);
}
if (nFreatico === true) {
const dummy = {
lat: lat,
lng: lng,
nFreatico: nFreatico,
inFreatico: inFreatico,
table: table,
};
surveys.push(dummy);
} else {
const dummy = {
lat: lat,
lng: lng,
nFreatico: nFreatico,
inFreatico: 0,
table: table,
};
surveys.push(dummy);
};
setSurveys([...surveys]);
incrementCount();
notificationHandler(1, 'Sondagem submetida com sucesso', notificationDispatch);
surveyLengthHandler(surveys.length, surveyDispatch);
}
};
const handleClearSurveys = (event: { preventDefault: () => void; }) => {
event.preventDefault();
setSurveys([]);
resetCount();
};
const handleFileInput = (e: any, type: number) => {
e.preventDefault();
const file = e.target.files[0];
if (type === 1) {
if (file) {
if (file.type === 'application/pdf') {
setSelectedFile(file);
} else {
notificationHandler(
2,
'Por favor seleciona um ficheiro tipo PDF',
notificationDispatch,
);
}
}
} else if (selectedImage) {
setSelectedImage(file);
}
// else {
// notificationHandler(
// 2,
// 'Por favor seleciona uma imagem do tipo PNG ou JPEG',
// notificationDispatch
// );
//}
};
const handleUploadPDF = (e: any, filename: string) => {
e.preventDefault();
if (selectedFile) {
const formPDFData = new FormData();
formPDFData.append('filename', filename);
formPDFData.append('file', selectedFile);
fetch(process.env.REACT_APP_BACKEND + '/api/drive', {
method: 'POST',
body: formPDFData
});
//.then((response) => (console.log(response)))
//.catch((error) => console.log(error));
}
};
const handleUploadImage = (e: any, filename: string) => {
e.preventDefault();
if (selectedImage) {
const formImgData = new FormData();
// formImgData.append('filename', filename);
formImgData.append('image', selectedImage);
// fetch(process.env.REACT_APP_BACKEND + '/api/drive/image', {
// method: 'POST',
// body: formImgData,
// })
// .then((response) => console.log(response))
// .catch((error) => console.log(error));
}
};
const handleSubmitForm = async (e: { preventDefault: () => void; }) => {
e.preventDefault();
if (!surveys[0]) {
notificationHandler(
2,
'Não é possível submeter sem qualquer sondagem',
notificationDispatch
);
}
const lat = surveys[0].lat;
const lng = surveys[surveys.length - 1].lng;
const { adress, placeId } = await formSubmitGeoCoder(Number(lat), Number(lng), notificationDispatch);
if(email) {
if (selectedFile) {
const fileName = selectedFile.name;
const editedName = fileName.substring(0, fileName.length - 4);
if (selectedImage) {
// const oldImgName = selectedImage.name;
// const imgExtension = oldImgName.substring(oldImgName.length - 4, oldImgName.length);
// const imgName = `logo_${editedName}${imgExtension}`;
formData = { adress, placeId, lat, lng, email, surveys, fileName, selectedImage };
} else {
formData = { adress, placeId, lat, lng, email, surveys, fileName };
}
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
};
const response = await fetch(process.env.REACT_APP_BACKEND + '/api/report', requestOptions);
if (response.ok) {
handleOpenThankYou();
setSurveys([]);
resetCount();
handleUploadPDF(e, selectedFile.name);
if (selectedImage) {
// const oldImgName = selectedImage.name;
// const imgExtension = oldImgName.substring(oldImgName.length - 4, oldImgName.length);
// const imgName = `logo_${editedName}${imgExtension}`;
handleUploadImage(e, selectedImage);
};
}
if (!response.ok) {
notificationHandler(
2,
'Erro',
notificationDispatch
);
}
} else {
notificationHandler(
2,
'Para validação do relatório iremos necessitar do mesmo em formato PDF',
notificationDispatch
);
}
} else {
notificationHandler(
2,
'Insira um e-mail para poder submeter os dados',
notificationDispatch
);
}
};
return (
<>
<div className='flex flex-col w-full h-full'>
<Navbar setPointDataArray={setPointDataArray} setDistanceBetween={setDistanceBetween} />
{showSliderForms ? (
<>
<div className='z-30'>
<div className='bg-white sm:absolute sm:right-0 h-auto max-h-full sm:h-full sm:w-[40%] overflow-y-scroll'>
{showThankYouForms ? (
<>
<ThankYou />
<div className='justify-center mb-5 items-center flex space-x-2'>
<button
className='bg-blue-helica w-40 h-10 rounded-md text-xs text-white p-1'
onClick={handleSubmitNew}
>
Submeter novo estudo
</button>
<button
className='bg-blue-helica w-40 h-10 rounded-md text-xs text-white p-1'
onClick={handleExit}
>
Sair
</button>
</div>
</>
) : (
<>
<button
type='button'
className='bg-white border ml-4 mt-4'
onClick={handleOpenSlider}
>
<IconContext.Provider
value={{
color: '#000000',
className: 'h-6 sm:h-9 w-6 sm:w-9',
}}
>
<AiOutlineRight />
</IconContext.Provider>
</button>
<form className='h-auto'>
{showFileSubmit ? (
<>
<FileSubmit setEmail={setEmail} surveys={surveys} handleFileInput={handleFileInput} selectedFile={selectedFile} selectedImage={setSelectedImage} />
<div className='justify-center mb-5 items-center flex space-x-2'>
<button
className='bg-blue-helica w-40 h-10 rounded-md text-xs text-white p-1'
onClick={() => {
handleOpenFileSubmit();
}}
>
Passo Anterior
</button>
<button
className='bg-blue-helica w-40 h-10 rounded-md text-xs text-white p-1'
onClick={(event) => {
handleSubmitForm(event);
}}
>
Enviar
</button>
</div>
</>
) : (
<>
<Form handleOpenFileSubmit={handleOpenFileSubmit} handleAddSurvey={handleAddSurvey} handleClearSurveys={handleClearSurveys} surveyNumber={surveyNumber} />
</>
)}
</form>
</>
)}
</div>
</div>
</>
) : (
<>
{pointDataArray ? (
<>
<div className='z-30'>
<div className='bg-transparent right-[35%] absolute top-auto mt-3 h-auto max-h-full w-auto'>
<button
type='button'
className='bg-white border'
onClick={handleOpenSlider}
>
<IconContext.Provider
value={{
color: '#000000',
className: 'h-6 sm:h-9 w-6 sm:w-9',
}}
>
<AiOutlineLeft />
</IconContext.Provider>
</button>
</div>
</div>
</>
) : (
<>
<div className='z-30'>
<div className='bg-transparent absolute right-4 top-auto mt-3 mr-12 h-auto max-h-full w-auto'>
<button
type='button'
className='bg-white border mr-2 align-top'
title='Recarregar pontos de sondagem'
>
<IconContext.Provider
value={{
color: '#000000',
className: 'h-8 sm:h-10 w-8 sm:w-10',
}}
>
<AiOutlineReload />
</IconContext.Provider>
</button>
<button
type='button'
className='bg-white border'
onClick={handleOpenSlider}
>
<IconContext.Provider
value={{
color: '#000000',
className: 'h-12 sm:h-14 w-12 sm:w-14',
}}
>
<AiOutlineLeft />
</IconContext.Provider>
</button>
</div>
</div>
</>
)}
</>
)}
{pointDataArray ? (
<>
<div className='z-30'>
<div className='bg-white absolute sm:right-0 h-auto max-h-full sm:h-full sm:w-1/3 overflow-y-scroll'>
<PointData pointDataArray={pointDataArray} distance={distanceBetween} handleClosePointData={handleClosePointData} map={map} />
</div>
</div>
</>
) : (null)}
</div>
</>
);
};
function addMarkers(
map: google.maps.Map | undefined,
reports: GeoReport[],
thisMarkers: google.maps.Marker[],
setThisMarkers: React.Dispatch<React.SetStateAction<google.maps.Marker[]>>,
setPointDataArray: React.Dispatch<React.SetStateAction<GeoReport | null>>,
) {
let isVisited = false;
const markers = reports.map((report: GeoReport) => {
const marker = new google.maps.Marker({
position: { lat: report.lat, lng: report.lng },
map: map,
icon: Icon,
});
marker.addListener('click', () => {
isVisited = !isVisited;
if (isVisited === true) {
setPointDataArray(report);
}
});
thisMarkers.push(marker);
setThisMarkers([...thisMarkers]);
new MarkerClusterer({
markers: thisMarkers,
map,
algorithm: new SuperClusterAlgorithm({ radius: 200 }),
});
return marker;
});
}
const addCircles = (
map: google.maps.Map | undefined,
setPointDataArray: React.Dispatch<React.SetStateAction<GeoReport | null>>,
reports: GeoReport[], circles: google.maps.Circle[],
setCircles: React.Dispatch<SetStateAction<google.maps.Circle[]>>,
) => {
let isVisited = false;
const addresses: string[] = [];
reports.map((report: GeoReport) => {
report.surveys.map(survey => {
survey.table.map(data => {
addresses.push(data.soilType);
});
});
let color = '#FFFFFFF';
const soilType = findMode(addresses);
if (soilType) {
color = getSoilTypeColor(soilType);
}
const marker = new google.maps.Circle({
center: { lat: report.lat, lng: report.lng },
radius: 500,
strokeColor: color,
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: color,
fillOpacity: 0.35,
map,
});
marker.addListener('click', () => {
isVisited = !isVisited;
if (isVisited === true) {
setPointDataArray(report);
}
});
circles.push(marker);
setCircles([...circles]);
return marker;
});
};
export default TestMap;
You can see here what I'm referring to! (I'm refreshing the website so it shows the problem)
Thanks in advance for any help!