js-api-loader icon indicating copy to clipboard operation
js-api-loader copied to clipboard

google is not defined

Open ernanni opened this issue 2 years ago • 3 comments

I'm implementing the Map feature in an nextjs personal project of mine.

I've imported the script on _app.js and did as the documentation demonstrates.

Although when I run the Map component the following error appears.

image

I've tried to workaround this issue by passing the api key inside the script tag itself but didn't work.

ernanni avatar Jul 18 '23 02:07 ernanni

If you would like to upvote the priority of this issue, please comment below or react with :+1: so we can see what is popular when we triage.

@ernanni Thank you for opening this issue. 🙏 Please check out these other resources that might help you get to a resolution in the meantime:

This is an automated message, feel free to ignore.

wangela avatar Jul 18 '23 02:07 wangela

Have you tried window.google?

rosc0 avatar Jul 20 '23 05:07 rosc0

What seemed to work for me is

this lib just loads google script, but for example google.maps.marker.AdvancedElement is not loaded before google maps init, so sometimes there is race condition with can not read AdvancedElement of undefined

The solution

create global Event, which is called after google map is initiated

if (typeof window !== 'undefined') {
  const mapsLoaded = new Event('googleMapsLoaded')
  // @ts-ignore
  window.initGoogleMap = () => {
    window.dispatchEvent(mapsLoaded)
  }
}
import useLoadExternalScript, {
  TScriptStatus as ScriptLoadingStatus,
} from '@ui/hooks/useLoadExternalScript'
import { useEffect, useState } from 'react'

const useLoadGoogleMaps = (apiKey: string) => {
  const scriptStatus = useLoadExternalScript(
    `https://maps.googleapis.com/maps/api/js?key=${apiKey}&v=beta&libraries=places,marker&callback=initGoogleMap`
  )
  const [status, setStatus] = useState<ScriptLoadingStatus>(scriptStatus)

  // set status ready after google maps init
  useEffect(() => {
    const afterInit = () => setStatus('ready')
    if (status !== 'ready') {
      window.addEventListener('googleMapsLoaded', afterInit)
    }
    return () => {
      window.removeEventListener('googleMapsLoaded', afterInit)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [setStatus])

  // set status error
  useEffect(() => {
    if (status !== 'ready') {
      setStatus(scriptStatus)
    }
  }, [scriptStatus, setStatus, status])

  return status
}

export default useLoadGoogleMaps

Caveat

there is possibly next race condition, if the script loads faster and sets the status to ready, the status is already 'ready' and there is the same issue, but I don't care anymore, because it would take me too much time to handle it, if you have solution, please provide, I am ready to improve my code

What this LIb is missing

you have to handle loading the script only once yourself, I thought this lib will provide exactly this functionality, instead I implemented that on my own and I am loading all the libs at once because of this, I would appreciate, if I just could use this and it would solve all those details. I don't see any benefits in using this lib

andylacko avatar Jul 20 '23 14:07 andylacko