t49tran / react-google-recaptcha-v3

Google Recaptcha V3 integration for React
MIT License
435 stars 91 forks source link

GoogleReCaptcha Context has not yet been implemented #73

Open trevorr opened 3 years ago

trevorr commented 3 years ago

Thanks for creating this library! I have run into a small issue however, and I'm curious whether my use case is supported.

The executeRecaptcha function returned by useGoogleReCaptcha throws a GoogleReCaptcha Context has not yet been implemented error when I try to use GoogleReCaptchaProvider on specific pages, rather than the entire app (in ReactDOM.render). I only want to use it on my app's signup and forgot password pages, because I don't want Google's reCAPTCHA logo in the lower right corner of every page of the app. (It's especially in the way on mobile.)

I'm guessing this is a consequence of using React Context, so you can't use the provider and the hook/consumer in the same component. If so, I think the error message could be improved, such as You can only call useGoogleReCaptcha in a component that is wrapped in GoogleReCaptchaProvider. It would also be helpful if the documentation provided guidance or an example of using the library on specific pages.

import React from 'react';
import { GoogleReCaptchaProvider, useGoogleReCaptcha } from 'react-google-recaptcha-v3';

const { REACT_APP_RECAPTCHA_SITE_KEY } = process.env;

export default function SignupPage(): React.ReactElement {
  const { executeRecaptcha } = useGoogleReCaptcha();
  const onSubmit = React.useCallback(
    (event: React.FormEvent) => {
      event.preventDefault();
      if (!executeRecaptcha) return;
      (async () => {
        try {
          const token = await executeRecaptcha('signup'); // throws context error
          // ...
        } catch (e) {
          // ...
        }
      })();
    },
    [executeRecaptcha]
  );
  return (
    <GoogleReCaptchaProvider reCaptchaKey={REACT_APP_RECAPTCHA_SITE_KEY} scriptProps={{ async: true }}>
      <form onSubmit={onSubmit} noValidate>
        {/* ... */}
      </form>
    </GoogleReCaptchaProvider>
  );
}

This version seems to work:

export default function SignupPage(): React.ReactElement {
  return (
    <GoogleReCaptchaProvider reCaptchaKey={REACT_APP_RECAPTCHA_SITE_KEY} scriptProps={{ async: true }}>
      <SignupForm />
    </GoogleReCaptchaProvider>
  );
}

function SignupForm(): React.ReactElement {
  const { executeRecaptcha } = useGoogleReCaptcha();
  const onSubmit = React.useCallback(
    (event: React.FormEvent) => {
      event.preventDefault();
      if (!executeRecaptcha) return;
      (async () => {
        try {
          const token = await executeRecaptcha('signup');
          // ...
        } catch (e) {
          // ...
        }
      })();
    },
    [executeRecaptcha]
  );
  return (
    <form onSubmit={onSubmit} noValidate>
      {/* ... */}
    </form>
  );
}
t49tran commented 3 years ago

Hi @trevorr, your second implementation is correct, while the first one is flawed. Obviously with React context, you can only access the context within a child component that wrapped by the context provider.

Actually, the question you raised had been asked and answered before. It's more related to how React context works rather than a problem library itself.

Having say that, I agree that it should be a nicer developer experience if you don't need to create an extra child component to work with GoogleRecaptchaProvider. I think I have an idea but just need time to test / implement it.

Regarding the error message, that is a good idea, I will add the explanation about the necessary to call the hook from a wrapped component there.

About adding guidance about how to implement this on a specific page, I think it's not really necessary. Most developers can figure it out either straight away or after a while. I prefer leaving it to developers to make use of this library in ways they deem fit rather than telling them what they should do.

I will keep this issue opened till we update the error message and update GoogleRecaptchaProvider.

Cheers.

t49tran commented 3 years ago

Btw @trevorr , if you want to update the error message, you're more than welcome to create a PR.

I am thinking about something along those lines: GoogleRecaptcha Context has not been implemented. If you are using the useGoogleRecaptcha hook, please check that it is called from a component wrapped inside GoogleReCaptchaProvider.

We also have a HOC so it should be nice to add something there to cover it as well.

phatNfqAsia commented 3 years ago

Hello @t49tran , based on @trevorr example I suppose the snippet in https://github.com/t49tran/react-google-recaptcha-v3#react-hook-usegooglerecaptcha-recommended-approach should be updated right? with empty dependencies this will also throw Execute recaptcha not yet available . It should be written this way:

import {
  GoogleReCaptchaProvider,
  useGoogleReCaptcha
} from 'react-google-recaptcha-v3';

const YourReCaptchaComponent = () => {
  const { executeRecaptcha } = useGoogleReCaptcha();

  // Create an event handler so you can call the verification on button click event or form submit
  const handleReCaptchaVerify = useCallback(async () => {
    if (!executeRecaptcha) {
      console.log('Execute recaptcha not yet available');
      return;
    }

    const token = await executeRecaptcha('yourAction');
    // Do whatever you want with the token
+  }, [executeRecaptcha]);

  // You can use useEffect to trigger the verification as soon as the component being loaded
  useEffect(() => {
    handleReCaptchaVerify();
  }, [handleReCaptchaVerify]);

  return <button onClick={handleRecaptchaVerify}>Verify recaptcha</button>;
};

ReactDom.render(
  <GoogleReCaptchaProvider reCaptchaKey="[Your recaptcha key]">
    <YourReCaptchaComponent />
  </GoogleReCaptchaProvider>,
  document.getElementById('app')
);
t49tran commented 3 years ago

Hi @phatNfqAsia, thanks for bringing this up. Yes, you are right, I will update the document.

nadirefe commented 2 years ago

@t49tran I believe you did not update the document yet.

victor-cui-brigit commented 2 years ago

is there a way to hide the recaptcha logo on the bottom right corner?

biniyammoges commented 2 years ago

is there a way to hide the recaptcha logo on the bottom right corner?

add these code to your css

.grecaptcha-badge {
   display: none;
}
Kameal22 commented 1 year ago

Doing exactly the same code in my Next js and it still throws the error: image

nizzie-2204 commented 1 year ago

Doing exactly the same code in my Next js and it still throws the error: image

you can downgrade to version 1.9.7, it worked for me

VietNguyenR commented 1 year ago

Doing exactly the same code in my Next js and it still throws the error: image

you can downgrade to version 1.9.7, it worked for me

Thank you, you save my days

developer-773 commented 1 year ago

is there a way to hide the recaptcha logo on the bottom right corner?

add these code to your css

.grecaptcha-badge {
   display: none;
}

When set style display none, recaptcha is not working, because, display none property can removed element from DOM. I recommend visibility hidden