JustFly1984 / react-google-maps-api

React Google Maps API
MIT License
1.82k stars 440 forks source link

False performance warning about libraries #238

Closed si4dev closed 5 years ago

si4dev commented 5 years ago

I'm using a library with useLoadScript like this:

const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: GOOGLE_API_KEY,
    libraries: ['places'],
  });

And on rerender it always shows: Performance warning! Loadscript has been reloaded unintentionally! You should not pass libraries prop as new array. Please keep an array of libraries as static class property for Components and PureComponents, or just a const variable outside of component, or somewhere in config files or ENV variables

I've reviewed the code and it seems to use wrong comparison for arrays. When I change libraries !== prevLibraries.current into JSON.stringify(libraries) !== JSON.stringify(prevLibraries.current) then it works as expected without performance warnings. However the real solution is probably more complex as the useEffect shouldn't trigger on the array libraries as it is not changed.

si4dev commented 5 years ago

Reading more on useEffect() and the hints in the warning learned me to define libraries outside the functional component like this: const libraries = ['places'];

And then use this

const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: GOOGLE_API_KEY,
    libraries,
  });

which will prevent useEffect() from triggering each rerender. So with this my issues is solved.

tonyjaimep commented 3 years ago

I tried what @si4dev suggested and the warning did not go away, however, when using

const [ libraries ] = useState(['places']);

the warning goes away.

ahmedyounes commented 3 years ago

I tried what @si4dev suggested and the warning did not go away, however, when using

const [ libraries ] = useState(['places']);

the warning goes away.

indeed

SachinMaharana commented 3 years ago

Having the same issue. What if we use useRef?

const libraries = ['places']
..
..
const libraries = ['places']

const App = () => {
   let libRef = React.useRef(libraries)

  const {isLoaded, loadError} = useJsApiLoader({
    googleMapsApiKey: process.env.REACT_APP_MAP_API,
    libraries: libRef.current,
  })

 }

The warning went away, but i was wondering if there something wrong with this?

JustFly1984 commented 3 years ago

@SachinMaharana Why so complicated? use just:

const libraries = ['places']

function App() {
  const {isLoaded, loadError} = useJsApiLoader({
    googleMapsApiKey: process.env.REACT_APP_MAP_API,
    libraries,
  })

  return isLoaded ? <GoogleMap /> : <></>
 }

 export default React.memo(App)
Shmehade321 commented 3 years ago

I have tried all of this but none of them is working for me. Any thoughts?

HYun66 commented 3 years ago

const libraries: Libraries = ['drawing']

const {isLoaded} = useJsApiLoader({ id: 'google-map-script', googleMapsApiKey: 'Your API Key', libraries: libraries, });

lucksp commented 3 years ago

Libraries

when importing the Libraries typing, it results in an error:

Unable to resolve path to module '@react-google-maps/api/dist/utils/make-load-script-url'.

Is there a better way to define the TypeScript typing of Libraries?

JustFly1984 commented 3 years ago

please provide minimal reproduction in codesandbox.io

HYun66 commented 3 years ago

Libraries

when importing the Libraries typing, it results in an error:

Unable to resolve path to module '@react-google-maps/api/dist/utils/make-load-script-url'.

Is there a better way to define the TypeScript typing of Libraries?

I didn't meet this error, but you can also try: type Libraries = ("drawing | "geometry" | "localContext" | "places" | visualization)[];

lucksp commented 3 years ago

please provide minimal reproduction in codesandbox.io

I'll see what I can do...in the interim here's a screenshot:

image

ericNodeone commented 2 years ago

For TypeSctipt if you need! outside component: export const libraries = String(['places']); And inside: const { isLoaded, loadError } = useLoadScript({ googleMapsApiKey: GOOGLE_MAP_API,

});

rolandihms commented 2 years ago

const [ libraries ] = useState(['places']);

After quite some headbanging and disregarding the useState hook as a vialble solution It has been solved by holding libraries array in state. Thanks @tonyjaimep

ahmedibra28 commented 2 years ago

const [ libraries ] = useState(['places']);

This one has solved my issue

JustFly1984 commented 2 years ago

you can just move it out of the component as const libraries = ['places'];

Risyandi commented 2 years ago

I found this warning, thank you everyone this issue is solved, with your all answer and solution.

ghost commented 2 years ago

Reading more on useEffect() and the hints in the warning learned me to define libraries outside the functional component like this: const libraries = ['places'];

And then use this

const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: GOOGLE_API_KEY,
    libraries,
  });

which will prevent useEffect() from triggering each rerender. So with this my issues is solved.

both of the solutions worked for me... Either define const libraries = ['places'] outside component or define const [libraries] = useState(['places']) inside component.

raja-muhammad-asher commented 2 years ago

const libraries = ['places'] outside component resolved my problem

joy7426 commented 2 years ago

const libraries: ("drawing" | "geometry" | "localContext" | "places" | "visualization")[] = ["places"];

Using this variable outside the component fixed my issue!

I am using NextJS 13 with Typescript

gitmccoyabrasaldo commented 2 years ago

I am using React TypeScript, it works! but idk if it is a good practice. Please can someone enlighten me!

Screenshot 2022-11-19 224415

Jukes312 commented 1 year ago

const libraries = ['places']; Declaring it outside a functional component worked for me.

However I wonder why it actually came up with error in the first place.

JustFly1984 commented 1 year ago

@Jukes312 cos you should not pass new objects/arrays/functions as props directly, without caching it, else you causing a redirect. It is javascript reference comparison issue.

Jukes312 commented 1 year ago

Oh thanks for the reply I was actually looking deep into it and figured it out. You are right about reference comparison issue, It goes back difference between primitive and reference types and how react rendering works. I found another way to solve the problem which was using the useMemo hook.

Screenshot 2022-12-30 at 20 30 02
JustFly1984 commented 1 year ago

@Jukes312 useMemo is over-engineering. simple const outside of function scope is enough.

Jukes312 commented 1 year ago

That's true thanks for the help 👊🏽

winniepukki commented 1 year ago

For typescript:

import type { Libraries } from '@googlemaps/js-api-loader';

const libraries = useRef<Libraries>(['places']);

const { isLoaded } = useLoadScript({
  libraries: libraries.current
});

or

import type { Libraries } from '@googlemaps/js-api-loader';

const libraries = useMemo<Libraries>(() => (['places']), []);

const { isLoaded } = useLoadScript({
  libraries: libraries.current
});
edwinvrgs commented 1 year ago

If you are using typescript, this might be the easiest way to enforce type safety:

// SomeComponent.tsx
import type { LoadScriptProps } from "@react-google-maps/api"
import { useLoadScript } from "@react-google-maps/api"

const libraries: LoadScriptProps["libraries"] = ["places"]

const SomeComponent = () => {
    useLoadScript({
        googleMapsApiKey: env.GOOGLE_MAPS_API_KEY,
        libraries,
    })

  // Rest of the component
}
SeanMH8911 commented 1 year ago

All of the above do not work for me, I'm using the StandaloneSearchBox. Does anyone have any suggestions?

mkandan commented 1 year ago

@SeanMH8911 I was just about to reply the exact same, I'm using StandaloneSearchBox and useJsApiLoader, but I forgot to put const libraries = ['places'] OUTSIDE the functional component, like literally underneath the import { StandaloneSearchBox, useJsApiLoader } from '@react-google-maps/api'

dejan966 commented 1 year ago

@winniepukki

Tysm. This is what finally did it for me. The solutions from the other comments like [libraries]=libraries fixed the rerendering but I wasn't able to use the library I needed. This is what finally worked

Houyem9 commented 1 year ago

For TypeSctipt if you need! outside component: export const libraries = String(['places']); And inside: const { isLoaded, loadError } = useLoadScript({ googleMapsApiKey: GOOGLE_MAP_API, [libraries]: libraries, });

This one has solved my issue

abura1han commented 1 year ago

Reading more on useEffect() and the hints in the warning learned me to define libraries outside the functional component like this: const libraries = ['places'];

And then use this

const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: GOOGLE_API_KEY,
    libraries,
  });

which will prevent useEffect() from triggering each rerender. So with this my issues is solved.

This technique resolved my issue

andrewtch88 commented 10 months ago

For typescript:

import type { Libraries } from '@googlemaps/js-api-loader';

const libraries = useRef<Libraries>(['places']);

const { isLoaded } = useLoadScript({
  libraries: libraries.current
});

or

import type { Libraries } from '@googlemaps/js-api-loader';

const libraries = useMemo<Libraries>(() => (['places']), []);

const { isLoaded } = useLoadScript({
  libraries: libraries.current
});

thanks this helped for TS users. i am using useJsApiLoader, it worked too. life saver.

nicitaacom commented 10 months ago

I have tried all of this but none of them is working for me. Any thoughts?

This helped me

"use client"

import { Libraries, useLoadScript } from "@react-google-maps/api"
import { PlacesAutoComplete } from "./components/PlacesAutoComplete"
import { useRef } from "react"

export default function Home() {
  // dance arounding to fix stupid performance warning
  const libraries = ["places"] as Libraries
  const libRef = useRef(libraries)
  const { isLoaded } = useLoadScript({
    googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY,
    libraries: libRef.current,
  })

  return (
    <div>
      {isLoaded ? (
        <>
          <PlacesAutoComplete />
        </>
      ) : (
        <div>loading...</div>
      )}
    </div>
  )
}

This approach work as well

  const libraries = useRef<Libraries>(["places"])
  const { isLoaded } = useLoadScript({
    googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY,
    libraries: libraries.current,
  })
deepwest8888 commented 10 months ago

What if i have google map and parts of it like distance tracking stuff all over places, i should use "useLoadScript" multiple times or should i use it only on top level of my app?

nicitaacom commented 10 months ago

What if i have google map and parts of it like distance tracking stuff all over places, i should use "useLoadScript" multiple times or should i use it only on top level of my app?

Try to use it once - in case it doesn't work when you use it once - use it twice

wesleygomesc6 commented 1 week ago

I tried what @si4dev suggested and the warning did not go away, however, when using

const [ libraries ] = useState(['places']);

the warning goes away.

This approach solved it for me