I’m working on an App that needs to load the "https://accounts.google.com/gsi/client" api, I used the Script component of NextJS, when try to navigate using the Link from NextJS, the script doesn’t load again. But if enter the url directly on the browser it fetch and load everything. I don’t know why the Link is not working properly.
The Script is on a ReactContext that manages the load of the Google script.
This is the Context and context provider
import { googleGsiClient } from '@/utils';
import Script from 'next/script';
import React, { createContext, useEffect, useState } from 'react';
export interface IGoogleSignInContext {
scriptLoaded: boolean;
scriptError?: string;
}
export const GoogleSignInContext = createContext<IGoogleSignInContext>({
scriptLoaded: false,
});
export const GoogleSignInProvider: React.FC<{
children: JSX.Element | JSX.Element[];
}> = ({ children }) => {
const [state, setState] = useState<IGoogleSignInContext>({
scriptLoaded: false,
});
const handleScriptLoaded = () => {
console.log('GOOGLE SCRIPT LOADED')
setState((actual) => ({
...actual,
scriptLoaded: true,
}));
};
const handleScriptError = () => {
setState((actual) => ({
...actual,
scriptLoaded: false,
scriptError: 'Failed to load script',
}));
};
useEffect(() => {
console.log('MOUNTING THE GOOGLE PROVIDER');
}, []);
return (
<>
<GoogleSignInContext.Provider value={state}>
<Script
src={googleGsiClient}
onLoad={handleScriptLoaded}
onError={handleScriptError}
/>
{children}
</GoogleSignInContext.Provider>
</>
);
};
This is my google button Component
import { GoogleSignInContext } from '@/context/GoogleSignIn'
import { GoogleCredentialResponse } from '@/interfaces';
import React, { useContext, useEffect, useRef } from 'react'
export interface GoogleSignInButtonProps {
onSingIn?: (credentials: GoogleCredentialResponse) => void;
calcWidth: number | null;
}
export const GoogleSignInButton: React.FC<GoogleSignInButtonProps> = ({
onSingIn,
calcWidth
}) => {
const googleContext = useContext(GoogleSignInContext);
const buttonRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if(!googleContext.scriptLoaded) return;
console.log(calcWidth);
if(!buttonRef) return;
if(!calcWidth) return;
window.google?.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CLIENTID,
callback(credentialResponse: GoogleCredentialResponse) {
onSingIn?.(credentialResponse);
},
});
window.google?.accounts.id.renderButton(buttonRef.current!, {
type: 'standard',
shape: 'rectangular',
size: 'large',
theme: 'outline',
locale: "en_US",
width: calcWidth.toString(),
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [googleContext.scriptLoaded, calcWidth, buttonRef])
if(!googleContext.scriptLoaded || !calcWidth) return <></>;
return (
<div ref={buttonRef} id='google-button' style={{height: 45}}></div>
)
}
My login and register are different pages. So i can nagivate between those with the Link
Portion of code in Login to navigate to register
<div className="mt-5 d-flex justify-content-around">
<small>Don't have an account?</small>
<small>
<Link href={"/auth/register"} prefetch={false}>Create account</Link>
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
{/* <a href='/auth/register'>Sign In</a> */}
</small>
</div>
Portion of code in Register to navigate to login
<div className="mt-5 d-flex justify-content-around">
<small>Already have an account? </small>
<small>
<Link href={'/auth'} prefetch={false}>Sign In</Link>
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
{/* <a href='/auth'>Sign In</a> */}
</small>
</div>
How can I use the NextJS Link and make the load of the script working? Thanks.
2
Answers
I've already found a solution, when the page is loaded for the first time, it fetch the script but then the script remain in the document no matter if you do navigation between pages. So in the context that manage the script I added a patch for check if the script is in the document when component is mounted, and do not load the Script component if exist because it causes some strange behaviour. This is my context now
Well, another way more straightforward is to use the onReady handler of the Script component, is called everytime the component is mounted and the script loaded
This works, when navigation occurs it calls the onReady and the script is already to use