We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
google.maps.Map is not a constructor
Hi,
I used react-google-maps-api months ago in our next application and I noticed an issue that happens sometimes when I refresh the page.
react-google-maps-api
I have this component:
"use client" import { defaultLocation } from "@/features/viewer/components" import { cn } from "@/lib/utils" import { GoogleMap, StreetViewPanorama } from "@react-google-maps/api" import { parseAsFloat, useQueryState } from "nuqs" import { ViewProps } from "./google-view-card" export const GoogleMapView = ({ viewMode, options }: ViewProps) => { const [lng] = useQueryState("lng", parseAsFloat.withDefault(defaultLocation[0])) const [lat] = useQueryState("lat", parseAsFloat.withDefault(defaultLocation[1])) const isStreetView = viewMode === "street" const isFullScreen = viewMode !== "both" return ( <GoogleMap mapContainerClassName={cn("flex-1 w-full h-full rounded-t-none rounded-b-2xl", isFullScreen ? "min-h-[680px]" : "min-h-0")} center={{ lat, lng }} options={{ ...options, tilt: isStreetView ? 0 : 45, mapTypeId: isStreetView ? "roadmap" : "satellite", zoom: isStreetView ? 10 : 20, fullscreenControl: isFullScreen, zoomControl: isFullScreen, cameraControl: true, }} > <StreetViewPanorama options={{ addressControl: false, fullscreenControl: isFullScreen, zoomControl: isFullScreen, position: { lat, lng }, visible: isStreetView, motionTracking: false, motionTrackingControl: false, }} /> </GoogleMap> ) }
This component is used here:
import { cn } from "@/lib/utils" import { Bird, CircleSlash, Columns2, Footprints, House, MapPinHouse, X, PanelLeftOpen, PanelRightOpen } from "lucide-react" import { parseAsBoolean, parseAsInteger, useQueryState } from "nuqs" import { useSelector } from "react-redux" import { RootState } from "@/redux/store" import { BuildingDataFeedback } from "@/components/building-data-feedback" import { GoogleMapView } from "@/components/google-map-view" import { GoogleViewCard, ViewMode } from "@/components/google-view-card" import { Badge } from "@/components/ui/badge" import { motion } from "framer-motion" import { CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group" import { BuildingIndexToggle } from "./building-index-toggle" import { BasicInfo } from "./infos/base" import { ParcelInfo } from "./infos/parcel" import { RoofInfo } from "./infos/roof-supestructure" import { SurroundingContextInfo } from "./infos/surrounding-context" import { ValuationInfo } from "./infos/valuation" import { Button } from "@/components/ui/button" export const BuildingInformationView = () => { const { buildingInformation } = useSelector((state: RootState) => state.geometry) const { city, houseNumber, street, zipCode } = useSelector((state: RootState) => state.address) const [buildingIndex] = useQueryState("buildingIndex", parseAsInteger.withDefault(-1)) const [footPrintVisible] = useQueryState("footPrintVisible") const [buildingVisible] = useQueryState("buildingVisible") const [isVisible, setIsVisible] = useQueryState("isVisible", parseAsBoolean.withDefault(true)) const [view, setView] = useQueryState<ViewMode | null>("view", { defaultValue: null, parse: (value) => value as ViewMode | null, }) const toggleVisibility = () => { setIsVisible(!isVisible); }; if ( !buildingInformation || buildingInformation.length === 0 || footPrintVisible === null || buildingVisible === null ) { return null } const building = buildingInformation[buildingIndex] return ( <div className={cn('flex h-full gap-4 w-full', !isVisible && "w-8")}> <div className={cn( 'flex h-full justify-between z-40 rounded-2xl bg-black/30 backdrop-blur-2xl border-none min-w-[480px] overflow-y-auto', !isVisible && 'min-w-[72px] overflow-hidden' )} data-testid='building-information-view' > <BuildingIndexToggle buildingCount={buildingInformation.length} /> <Button onClick={toggleVisibility} className=" bg-transparent absolute left-4 bottom-2 rounded-full text-sm z-50 w-10 h-10 p-2 hover:bg-black/20"> {isVisible ? ( <PanelRightOpen className="text-white" size={20}/> ) : ( <PanelLeftOpen className="text-white" size={20}/> )} </Button> <motion.div className='rounded-r-2xl rounded-l-none bg-black/10 backdrop-blur-2xl border-transparent overflow-y-auto building-information-view-scrollbar w-full' animate={{ opacity: isVisible ? 1 : 0, }} transition={{ duration: 0.2 }} > <CardHeader className='relative flex flex-col gap-2'> <div className='flex absolute items-center gap-2 top-3 right-3'> <Badge className='bg-rose-500'>Neu</Badge> </div> <div> <CardTitle className='text-white text-lg'>Gebäudeinformationen</CardTitle> <CardDescription className='text-white/70'>Wechseln Sie links zwischen den Gebäude-IDs</CardDescription> </div> <div className='border border-white/10 p-4 rounded-lg flex items-center gap-4 text-white'> <MapPinHouse /> <h2> {street} {houseNumber}, {zipCode} {city} </h2> </div> </CardHeader> <CardContent className='flex flex-col mt-3'> <GoogleViewCard /> <div className='w-full'> {building ? ( <div className='flex flex-col gap-0 h-full'> <BasicInfo buildingInfo={building.buildingInformation} /> <ValuationInfo valuationInfo={building.valuationInformation.buildingInformation} /> <SurroundingContextInfo contextInfo={{ surroundingContext: building.surroundingContext }} /> <ParcelInfo parcels={building.parcelInformation.parcels} /> <RoofInfo roofInfo={building.roofSuperstructureInformation} /> </div> ) : ( <EmptyState /> )} </div> <BuildingDataFeedback /> </CardContent> </motion.div> </div> {/* Views- always rendered but visibility controlled */} <div className='flex flex-col w-full h-full flex-1'> {view && ( <div className='flex items-center justify-between p-2 border border-white/10 rounded-b-none rounded-t-2xl text-white z-50 bg-black/50 backdrop-blur-2xl' data-testid='google-views-toggle' > <ToggleGroup type='single' value={view as string} onValueChange={(value) => value && setView(value as ViewMode)} className='flex space-x-2' > <ToggleGroupItem value='street' className='data-[state=on]:bg-primary data-[state=on]:text-primary-foreground hover:bg-transparent hover:text-white/50 rounded-full min-w-24 gap-2' > <Footprints /> Straße </ToggleGroupItem> <ToggleGroupItem value='immersive' className='data-[state=on]:bg-primary data-[state=on]:text-primary-foreground hover:bg-transparent hover:text-white/50 rounded-full min-w-24 gap-2 z-50' data-testid='google-immersive-view-item' > <Bird /> Vogel </ToggleGroupItem> <ToggleGroupItem value='both' className='data-[state=on]:bg-primary data-[state=on]:text-primary-foreground hover:bg-transparent hover:text-white/50 rounded-full min-w-24 gap-2' > <Columns2 /> Beide </ToggleGroupItem> </ToggleGroup> <Button onClick={() => setView(null)} className='w-10 h-10 p-0 bg-white hover:bg-white/70 text-white/50 rounded-full'> <X className="text-black" size={22} /> </Button> </div> )} <div className={cn("relative flex flex-1 h-full w-full overflow-hidden z-50 p-2", !view && "hidden")} data-testid='google-map' > <div className='absolute top-0 left-0 h-full transition-all duration-300' style={{ opacity: view === "street" || view === "both" ? 1 : 0, pointerEvents: view === "street" || view === "both" ? "auto" : "none", width: view === "both" ? "calc(50% - 8px)" : "100%", // Adjusting width for gap marginRight: view === "both" ? "8px" : "0", // Adding gap }} > <GoogleMapView viewMode='street' /> </div> <div className='absolute top-0 right-0 h-full transition-all duration-300' style={{ opacity: view === "immersive" || view === "both" ? 1 : 0, pointerEvents: view === "immersive" || view === "both" ? "auto" : "none", width: view === "both" ? "calc(50% - 8px)" : "100%", marginLeft: view === "both" ? "8px" : "0", }} > <GoogleMapView viewMode='immersive' /> </div> </div> </div> </div> ) } const EmptyState = () => { return ( <div className='flex flex-col items-center text-center gap-6 w-full p-8 text-white'> <div className='relative'> <CircleSlash className='absolute -top-2 -left-2' /> <House size={60} strokeWidth={1} /> </div> <div className='flex flex-col gap-2'> <h2 className='text-lg'>Es ist kein Gebäude ausgewählt</h2> <p className='text-white/40 text-xs max-w-sm leading-5'> Wählen Sie Ihr Gebäude aus der Liste der Indexe aus, die Sie links sehen. Das ausgewählte Gebäude wird blau hervorgehoben. </p> </div> </div> ) }
I define the key in the layout ( I get the key through a server action ):
import { getEnv } from "@/actions/env" import "@/globals.css" import { cn } from "@/lib/utils" import { deDE } from "@/localization/auth/de-DE" import { ClerkProvider } from "@clerk/nextjs" import { Inter as FontSans } from "next/font/google" import Script from "next/script" import "normalize.css" import { NuqsAdapter } from "nuqs/adapters/next/app" import { Header } from "@/components/app-header" import { Providers } from "@/components/providers" import { Toaster as Sonnertoaster } from "@/components/ui/sonner" import { Toaster } from "@/components/ui/toaster" import { metadata } from "./meta" export const fontSans = FontSans({ subsets: ["latin"], variable: "--font-sans", }) export { metadata } export default async function RootLayout({ children }: { children: React.ReactNode }) { const env = await getEnv() const googleMapsKey = env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY! return ( <ClerkProvider localization={deDE} afterSignOutUrl='/' afterMultiSessionSingleSignOutUrl='/' publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} > <html lang='de' suppressHydrationWarning> <head> <link rel='apple-touch-icon' sizes='180x180' href='/favicons/apple-touch-icon.png' /> <link rel='icon' type='image/png' sizes='32x32' href='/favicons/favicon-32x32.png' /> <link rel='icon' type='image/png' sizes='16x16' href='/favicons/favicon-16x16.png' /> <Script src={`https://maps.googleapis.com/maps/api/js?key=${googleMapsKey}&loading=async`} strategy='beforeInteractive' /> </head> <NuqsAdapter> <body className={cn("min-h-screen bg-background font-sans antialiased overflow-x-hidden", fontSans.variable)}> <Providers> <div className='flex flex-col h-dvh'> <div className='fixed z-50 h-[64px] w-full flex justify-between'> <Header /> </div> <div className='flex-grow'>{children}</div> </div> </Providers> <Toaster /> <Sonnertoaster richColors /> </body> </NuqsAdapter> </html> </ClerkProvider> ) }
When I refresh the page sometimes, it says google.maps.Map is not a constructor:
google.maps.Map
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Hi,
I used
react-google-maps-api
months ago in our next application and I noticed an issue that happens sometimes when I refresh the page.I have this component:
This component is used here:
I define the key in the layout ( I get the key through a server action ):
When I refresh the page sometimes, it says
google.maps.Map
is not a constructor:The text was updated successfully, but these errors were encountered: