-
-
Notifications
You must be signed in to change notification settings - Fork 464
How to avoid 'Loader must not be called again with different options' error when using nonce in useJsApiLoader? #3023
-
Hello! I'm currently trying to make use of the nonce option in the useJsApiLoader hook.
After setting everything up it seemed to work as expected, but after I refresh the page and get a new nonce value, I end up with the error: Loader must not be called again with different options (I have double checked and the only difference in the options provided after refreshing is the nonce.)
I've read that the loader acts as a singleton and essentially no new instances are created are created as long as you pass the same settings, which makes sense to me, but in this case the nonce value has to be different (since it is a nonce).
I've seen from this issue that you could potentially reset the loader manually, which might help with getting around this, but since that would require using js-api-loader directly, I wouldn't be able to use the useJsApiLoader hook anymore.
It feels like I'm missing something, is there a better/recommended way to handle this scenario?
Many thanks in advance!
Editing to include my example in the comments:
https://stackblitz.com/edit/cd-rgmapi
Steps:
- Click 'Go to map' (map loads fine)
- Click 'Go home'
- Click 'Go to map' again (error appears)
Beta Was this translation helpful? Give feedback.
All reactions
Choose your preferred solution below.
- Reuse the same
noncevalue - Ensure calling
useJsApiLoaderonly once in your app - Load scripts manually without
useJsApiLoader(I don't recommend this)
In most cases, it's enough to load scripts only once to use Google Maps API. Twice or more would mean something technically wrong in your app.
Calling useJsApiLoader with the same nonce value, loading scripts will be just skipped, and the same API instance will be reused, so you don't worry about security holes related to loading scripts with known nonce value. And ensure calling useJsApiLoader only once in your app is a good choice too.
Sample: https://stackblitz.com/edit/cd-rgmapi-pnhl9b?file=App.tsx
I...
Replies: 12 comments 23 replies
-
@chrisdueck please provide minimal reproduction. If page re-renders, there should be completely new google maps instances, and useLoadScript should be called every time page refreshes, as completely new process. Not sure that it is related to page-refresh in your case.
Passible you need to wrap your options object into useMemo hook?
I could guess it is related with Strict mode or react@17 double executing component and calling the hook 2 times.
Beta Was this translation helpful? Give feedback.
All reactions
-
@chrisdueck hi you try set Id=yourId for all maps in all page you used, id the same
Beta Was this translation helpful? Give feedback.
All reactions
-
@Ho-Ngoc-Thanh I am only using one map in one page and adding an Id makes no difference. You can easily check this with my example above.
Beta Was this translation helpful? Give feedback.
All reactions
-
@JustFly1984 Do you have any further suggestions?
Beta Was this translation helpful? Give feedback.
All reactions
-
@chrisdueck I found a solution for your example giving in your link, the issue is with nonce as it don't work when you change the value of nonce
I tried two ways and is working in both
solution 1. So in MapPage.tsx set nonce static
setNonce(nonce-691d29db-ab00-4bf6-94fa-168373d2fb7e);
solution 2. comment out nonce in Map.tsx
const { isLoaded, loadError } = useJsApiLoader({
googleMapsApiKey: '',
// nonce: nonce,
});
Hope it helps
Beta Was this translation helpful? Give feedback.
All reactions
-
👎 1
-
@ahmedmajidgit "Just don't use the nonce" is not a solution. The whole point of this discussion is that I need to use it. It is necessary for the content security policy of my site and it won't work without it.
I'm not sure you understand its purpose - nonce means number used once, so it has to change value each time it is used, otherwise it is pointless. The example doesn't need it because it's just for demonstration - I even commented the code saying that the generation of the nonce is being faked for the sake of the example.
Both of your suggestions completely avoid the problem.
Beta Was this translation helpful? Give feedback.
All reactions
-
The problem is happening with me too, while trying on my own style, then copy pasting from the documentation and still would display the same error!
Hope someone could solve it.
Beta Was this translation helpful? Give feedback.
All reactions
-
Comment out or remove React.StrictMode in index.js file.
Beta Was this translation helpful? Give feedback.
All reactions
-
👎 1
-
Comment out or remove React.StrictMode in index.js file.
{/* <React.StrictMode> */}
{/* </React.StrictMode> */}
Beta Was this translation helpful? Give feedback.
All reactions
-
👎 10
-
In my case, the problem was happened every time I add or edit something in useJsApiLoader :
const { isLoaded } = useJsApiLoader({
id: "google-map-script",
googleMapsApiKey: "Google Map API Key",
libraries: ["places"],
});
Reload the server, solve the issue for me
so every time you change useJsApiLoader you need to reload the dev server
Ctrl C and yarn dev
Beta Was this translation helpful? Give feedback.
All reactions
-
👎 1
-
It sounds like you had a different problem to mine. The issue I'm facing can be replicated with my example above.
Beta Was this translation helpful? Give feedback.
All reactions
-
resetting the dev server fixed it for me. Thank you @TheYass1n
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1 -
👎 1 -
🎉 1
-
you have to extract libraries array into const outside of component scope.
Beta Was this translation helpful? Give feedback.
All reactions
-
Thank you, it worked for me!
Beta Was this translation helpful? Give feedback.
All reactions
-
Beta Was this translation helpful? Give feedback.
All reactions
-
👎 1
-
This is not the problem I am asking about. You're not even using a nonce here, and I only have one component as you can see in my example.
(...and did you really just thumbs up and heart your own answer?)
Beta Was this translation helpful? Give feedback.
All reactions
-
GoodLuck 👍
(...and yes I did, since they should not actually allow me to do that)
Beta Was this translation helpful? Give feedback.
All reactions
-
Choose your preferred solution below.
- Reuse the same
noncevalue - Ensure calling
useJsApiLoaderonly once in your app - Load scripts manually without
useJsApiLoader(I don't recommend this)
In most cases, it's enough to load scripts only once to use Google Maps API. Twice or more would mean something technically wrong in your app.
Calling useJsApiLoader with the same nonce value, loading scripts will be just skipped, and the same API instance will be reused, so you don't worry about security holes related to loading scripts with known nonce value. And ensure calling useJsApiLoader only once in your app is a good choice too.
Sample: https://stackblitz.com/edit/cd-rgmapi-pnhl9b?file=App.tsx
If you genuinely need to load API scripts multiple times with different nonce values each time because of your business requirements. In that case, you should load them without useJsApiLoader to not use @googlemaps/js-api-loader, dependee on @react-google-maps/api. Because @googlemaps/js-api-loader raise the error explicitly to avoid unexpected load duplication. This restriction applies even if it is the nonce parameter.
https://github.com/googlemaps/js-api-loader/blob/main/src/index.ts#L317
https://github.com/googlemaps/js-api-loader/blob/main/src/index.ts#L342
Beta Was this translation helpful? Give feedback.
All reactions
-
🎉 5
-
Thank you so much @kimitaka. This answer really helped me to understand the issue with my implementation.
Calling useJsApiLoader with the same nonce value, loading scripts will be just skipped
Reading this and realising from your example that I can simply move the nonce loading to the outer <App /> component, so it is only fetched on the very first load is what fixed the issue for me. It almost seems obvious now I look at it again!
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
in my nextjs project I use i18 framework.
i want to pass current locale (current active language) to language prop of useJsApiLoader, but if user changes the language exactly this error appears.
how would you recommend to use different language option without calling the loader again?
Beta Was this translation helpful? Give feedback.
All reactions
-
I think it's off-topic from chrisdueck's original question. I'll be able to help you if you post as your original.
Beta Was this translation helpful? Give feedback.
All reactions
-
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1
-
The issue with language is the reason this library exists in first place. I've made a LoadScript component which behaved correctly on language change, but it was 4 years ago. You can look at gatsby-example package for implementation details, but in short - you can do that.
Beta Was this translation helpful? Give feedback.
All reactions
-
nonce itself is "number once" so it should not be changed at runtime in the browser.
Nonce is set by the web server on both, header and html, and guaranties that the code running is the code provided by the server.
There should not be change of nonce for the document without page reload.
If you are doing SSR, nonce should change on navigation, cos it calls server with full page reload.
If you are doing SPA, nonce can't change on navigation, cos navigation is using only browser API, without calling the web server.
To get new nonce, both in headers and in html you have to call a web sever and get a fresh page.
Beta Was this translation helpful? Give feedback.
All reactions
-
can I just assign a string value that won't change at all, or it's not recommended ?
const { isLoaded, loadError } = useJsApiLoader({ googleMapsApiKey: `${googlePlacesKey}`, nonce: "map", libraries: ["places"], });
Beta Was this translation helpful? Give feedback.
All reactions
-
I faced the same problem when trying to set up I18n with the useJsApiLoader hook.
Found the solution here: #3168
Beta Was this translation helpful? Give feedback.
All reactions
-
I found the same problem, this one because I store my API keys in my backend, any workaround to this one? because useJsApiLoader cannot be used inside a conditional
Beta Was this translation helpful? Give feedback.
All reactions
-
@Novizh You can have whole component which uses useJsApiLoader rendered under conditional. Just lift your conditional up the component tree into parent's JSX
Beta Was this translation helpful? Give feedback.
All reactions
-
I assume I need to pass the API key as a prop to the child component?
Beta Was this translation helpful? Give feedback.
All reactions
-
@JustFly1984 it works, thanks mate!
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Just in case this helps anyone, after trying various things, what worked for me was clearing the cookies and site data. Seems like some config data was getting persisted that was causing the "Loader must not be called again with different options" error.
Beta Was this translation helpful? Give feedback.
All reactions
-
👎 2