Skip to content

Instantly share code, notes, and snippets.

@passosleo
Last active August 23, 2023 15:06
Show Gist options
  • Save passosleo/6e353e39891d7cfc84e0a6d973619a4b to your computer and use it in GitHub Desktop.
Save passosleo/6e353e39891d7cfc84e0a6d973619a4b to your computer and use it in GitHub Desktop.
React Google Maps Implementation
import React from "react";
import { useMap } from "@/components/Map/hooks/useMap";
import { Map, MarkerObject } from "@/components/Map";
export function Example() {
const { settings, zoomOut, zoomIn, panTo, directions } = useMap({
defaultCenter: {
lat: 0,
lng: 0,
},
defaultZoom: 15,
});
return (
<Map
settings={settings}
directions={directions}
markers={
[
{
position: { lat: 0, lng: 0 },
onClick: () => {
console.log("marker clicked");
zoomOut();
},
},
{
position: { lat: 0, lng: 0 },
onClick: () => {
console.log("marker clicked");
zoomIn();
},
},
{
position: { lat: 0, lng: 0 },
onClick: () => {
console.log("marker clicked");
panTo(
{ lat: 0, lng: 0 },
{
enableZoom: true,
zoom: 10,
timeout: 1000,
onFinish: () => {
console.log("pan to finished");
},
}
);
},
},
] as MarkerObject[]
}
/>
);
}
import React, { memo } from 'react';
import {
DirectionsRenderer,
GoogleMap,
GoogleMapProps,
Marker,
useJsApiLoader
} from '@react-google-maps/api';
type Position = {
lat: number;
lng: number;
};
export type MarkerObject = {
icon: string;
position: Position;
onClick?: (position: Position) => void;
};
type Props = {
markers: MarkerObject[];
settings: GoogleMapProps;
directions?: google.maps.DirectionsResult | null;
width?: string;
height?: string;
};
export function MapComponent({
markers,
settings,
directions,
width = '100%'
height = '100vh'
}: Props) {
const { isLoaded } = useJsApiLoader({
id: 'google-map-script',
googleMapsApiKey: 'YOUR-API-KEY'
});
return isLoaded ? (
<GoogleMap
mapContainerStyle={{ width, height }}
options={{ mapTypeControl: false }}
{...settings}
>
{markers?.map(({ icon, position, onClick }, index) => (
<Marker
key={index}
position={position}
icon={icon}
zIndex={index}
onClick={() => onClick?.(position)}
/>
))}
{directions && <DirectionsRenderer directions={directions} />}
</GoogleMap>
) : (
<></>
);
}
export const Map = memo(MapComponent);
import { GoogleMapProps } from '@react-google-maps/api';
import { useState, useEffect, useCallback } from 'react';
type Props = {
defaultCenter: GoogleMapProps['center'];
defaultZoom: GoogleMapProps['zoom'];
};
type GetRouteOptions = {
onRouteFetched: (result: google.maps.DirectionsResult) => void;
destination: google.maps.LatLngLiteral;
travelMode?: google.maps.TravelMode;
origin: google.maps.LatLngLiteral;
};
type PanToOptions = {
onFinish?: () => void;
enableZoom?: boolean;
timeout?: number;
zoom?: number;
};
export function useMap({ defaultCenter, defaultZoom }: Props) {
const [map, setMap] = useState<google.maps.Map | null>(null);
const initialSettings: GoogleMapProps = {
onLoad: useCallback((map: google.maps.Map) => setMap(map), []),
onUnmount: useCallback(() => setMap(null), []),
center: defaultCenter,
zoom: defaultZoom
};
const [settings] = useState<GoogleMapProps>(initialSettings);
const [userLocation, setUserLocation] =
useState<google.maps.LatLngLiteral | null>(null);
const [directions, setDirections] =
useState<google.maps.DirectionsResult | null>(null);
function getUserLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(({ coords }) => {
setUserLocation({
lat: coords.latitude,
lng: coords.longitude
});
});
}
}
function zoomOut(number: number = 7) {
if (!map) return;
map.setZoom(number);
}
function zoomIn(number: number = 8) {
if (!map) return;
map.setZoom(number);
}
function panTo(
{ lat, lng }: google.maps.LatLngLiteral,
{
timeout = 500,
enableZoom = false,
zoom = 18,
onFinish
}: PanToOptions = {}
) {
if (!map) return;
const currentCenter = map.getCenter();
if (currentCenter) {
const currentLat = currentCenter.lat().toFixed(3);
const currentLng = currentCenter.lng().toFixed(3);
const destinationLat = lat.toFixed(3);
const destinationLng = lng.toFixed(3);
if (currentLat === destinationLat && currentLng === destinationLng) {
if (onFinish) onFinish();
return;
}
}
map.panTo({ lat, lng });
if (!enableZoom && !onFinish) return;
setTimeout(() => {
if (enableZoom) zoomIn(zoom);
if (onFinish) onFinish();
}, timeout);
}
function getRoute({
origin,
destination,
travelMode = google.maps.TravelMode.DRIVING,
onRouteFetched
}: GetRouteOptions) {
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin,
destination,
travelMode
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK && result) {
onRouteFetched(result);
}
}
);
}
useEffect(() => {
getUserLocation();
}, []);
useEffect(() => {
if (userLocation) {
panTo(userLocation);
}
}, [userLocation]);
return {
setDirections,
userLocation,
directions,
getRoute,
settings,
zoomOut,
zoomIn,
panTo,
map
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment