Home > Blockchain >  Invalid hook call. Hooks can only be called inside of the body of a function component NextJS
Invalid hook call. Hooks can only be called inside of the body of a function component NextJS

Time:10-09

So, in my next app, i'm using react-google-one-tap-login library that has useGoogleOneTapLogin hooks and must be call inside of react component. But when i call it like:

func() {
  useGoogleOneTapLogin({})
  return (some jsx)
}

I got an error ReferenceError: window is not defined.

Then, i'll try to fix it using if (process.browser) then run useGoogleOneTapLogin, but i got an error: useGoogleOneTapLogin is called conditionally, hooks must be called in the exact same order in every component render. BUT, this is only occur when i'll try to deploy the app.

Then, once again i'll try to fix by call it inside useEffect for the first time comp load, by also got an error: Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

CodePudding user response:

https://github.com/MSalmanTariq/react-google-one-tap-login/issues/13#issuecomment-1056016843

import dynamic from 'next/dynamic'

const MyComponent = () => {
  const GoogleOneTapLogin = dynamic(() => import('react-google-one-tap-login'))
  
  return (
    <GoogleOneTapLogin
        one rror={(error) => console.log(error)}
        onSuccess={(response) => console.log(response)}
        googleAccountConfigs={{ client_id: 'example.apps.googleusercontent.com' }}
      />
   )
}

CodePudding user response:

You can't call hooks inside conditions per react documentation. See: https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

To solve your issue if your hook depends on window object or you want to client side rendering, you can delay rendering your component only on client side.

// Create a component called ClientOnly
// components/ClientOnly.js

import { useEffect, useState } from "react";

export default function ClientOnly({ children, ...rest}) {
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() => {
    setHasMounted(true);
  }, []);

  if (!hasMounted) {
    return null;
  }

  return <div {...rest}>{children}</div>;
}

then, wrap your component with this component

  <ClientOnly>
    <YourGoogleOneTapLoginComponent />
  </ClientOnly>

To read more about this pattern, See: https://www.joshwcomeau.com/react/the-perils-of-rehydration

  • Related