google-maps-react-hooks icon indicating copy to clipboard operation
google-maps-react-hooks copied to clipboard

Issues with markers on Google Maps API - not always loading

Open anafaria-slefty opened this issue 1 year ago • 0 comments

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!

anafaria-slefty avatar Dec 12 '23 13:12 anafaria-slefty