dozoisch / react-google-recaptcha

Component wrapper for Google reCAPTCHA
MIT License
1.03k stars 142 forks source link

Captcha is rendered just once on React 18 in concurrent mode #250

Open AlexKrupko opened 2 years ago

AlexKrupko commented 2 years ago

Captcha component is rendered just once in all application. It's not rendered in other component or conditionally. You can find issue example on CodeSandbox. Just empty div is rendered for subsequent captcha elements.

This issue relates to the concurrent mode introduced in React 18. It doesn't work as expected if app is wrapped in StrictMode on React 18 which enables concurrent mode.

react version: 18.2.0 react-google-recaptcha version: 2.1.0 react-async-script version: 1.2.0

syntaxerror05 commented 2 years ago

Same issue, is there any fix ? edit: the issue is only for development once the project gets live it will fixed the issue.

arugaz commented 2 years ago

same issue

gmorana commented 2 years ago

Same issue here

mchigit commented 2 years ago

Same issue here

brezhitskiy42 commented 2 years ago

Same issue with Next.js

rafakwolf commented 2 years ago

Same issue here, NextJS 12

wedelgaard commented 2 years ago

Yup, got the same issue :/

hartzis commented 2 years ago

Same issue, is there any fix ? edit: the issue is only for development once the project gets live it will fixed the issue.

@syntaxerror05 Great callout here!

@AlexKrupko thank you for the example codesandbox! I've been able to confirm this is only an issue with <StrictMode> in react 18. It sounds like it should only be an issue in development mode and is likely one of these

These both have this caveat:

This only applies to development mode.

AlexKrupko commented 2 years ago

Hi @hartzis , yes, now it does not work only in development mode. But that was enough for me to refuse using this library. It should work anywhere in my opinion. Concurrent mode is like an experimental feature in React 18. But they are going to introduce such behavior in further versions to allow React to add and remove sections of the UI while preserving state.

anajavi commented 2 years ago

I upgraded the react-google-recaptcha in the codesandbox to v3.0.0-alpha.1 and it seems to work in strictmode.

So maybe this is already fixed in the master?

ZaikoXander commented 2 years ago

Same issue ;-;

m061i6 commented 2 years ago

Same issue here, with NextJS only render first time.

sharadrb commented 1 year ago

I was having same issue for a long time. As suggested by anajavi, this issue is fixed in master branch and works in v3.00-alpha.1.

vishalthapa07 commented 1 year ago

BOOM! I have found the solution, push your code in production site, it gonna work for sure. I have also faced the same issue in my localhost and development site.

jbmgil commented 1 year ago

I upgraded the react-google-recaptcha in the codesandbox to v3.0.0-alpha.1 and it seems to work in strictmode.

So maybe this is already fixed in the master?

I did the same and it worked! It worked in development and in production. Out of every other possible solution that I found, this was the only one that fixed the problem so far.

valera-rozuvan commented 1 year ago

This is a known problem which affects projects with an old code base.

The issue is caused by newer version of React (v18.x) calling some functions twice (in development mode only!), so if there are side effects present, things can go wrong.

image

See Detecting unexpected side effects.

A temporary fix for this issue is to disable React strict mode in your project. I.e., in the code base which uses the package react-google-recaptcha.

If you have a place in your code that looks something like this:

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>,
  </React.StrictMode>,
);

to apply the temporary fix, you remove React.StrictMode, so it becomes like this:

root.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
);

But we should really ask @dozoisch to rewrite the code base of react-google-recaptcha using the latest best practices from React v18 docs.

This is a great project! Thank you @dozoisch !!! You are the best :-)

AirP0WeR commented 1 year ago

Same issue. In prod mode works OK for me.

mekdie commented 1 year ago

Yeah, everyone is right, it only does not work in development mode or with Strict mode ON. Once in production or non local host (development), it should work.

agmCorp commented 1 year ago

Same here. Any solution? It's very annoying not being able to work on development.

Kolby-Udacity commented 1 year ago

Almost a year later and still no solution?

brycehanscomb commented 1 year ago

@valera-rozuvan's explanation is correct about <StrictMode> causing a double-instantiation in development, and this lib is not built to be resilient to that.

You can avoid the issue by managing the lifecycle of the global grecaptcha object yourself and simply passing it into the component when it's available. This is mentioned in the docs:

ReCaptcha loading google recaptcha script manually.

Here's an example:

const useInstanceLoader = (siteKey: string) => {
  const helperInstance = useRef(null);

  useEffect(() => {
    if (!window.grecaptcha) {
      const script = document.createElement('script');
      script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
      script.async = true;
      script.defer = true;
      script.onload = () => {
        helperInstance.current = window.grecaptcha;
      };
      document.body.appendChild(script);
    } else {
      helperInstance.current = window.grecaptcha;
    }

    return () => {
      if (helperInstance.current) {
        helperInstance.current.reset();
      }
    };
  }, []);

  return helperInstance.current;
};

// later...
import ReCAPTCHA from "react-google-recaptcha";
const siteKey = process.env.NEXT_PUBLIC_RECAPTCHA_KEY;

export const MyApp = () => {
   const instance = useInstanceLoader(siteKey);

   return (
      <div>
          <ReCAPTCHA siteKey={siteKey} grecaptcha={instance} />
     </div>
   )
}
joaoguidev commented 1 year ago

Hello.

Im using Remix 1.19.3.

I tried everything here and nothing worked. Not even in production.

Curiously it renders fine only on the index route( myapp.com ) but on other routes it does not ( myapp.com/contact ).

brycehanscomb commented 1 year ago

@joaoguidev can you link a Stackblitz showing your reproduction?

127 commented 10 months ago

Same problem here in Remix 2.3.1

rfviolato commented 9 months ago

I was having similar issues as the report (empty div) and what solved for me was these 2 things:

To give some more context, I'm using Next v14.0.1, and I the ReCAPCTHA component is being mounted in a login modal after it is open. Closing and reopening the modal multiple times also works for me.

valera-rozuvan commented 8 months ago

@dozoisch any plans to resolve this issue in core?

There are the mentioned workarounds (see above in this discussion). However, would be nice to not have to resort to them.

Dylan-Kentish commented 6 months ago

I was having similar issues as the report (empty div) and what solved for me was these 2 things:

  • Using the default export instead of the named export. (import ReCAPTCHA from 'react-google-recaptcha'; instead of import { ReCAPTCHA } from 'react-google-recaptcha';
  • Upgrading from 2.1.0 to 3.1.0.

To give some more context, I'm using Next v14.0.1, and I the ReCAPCTHA component is being mounted in a login modal after it is open. Closing and reopening the modal multiple times also works for me.

Switching from the named export to the default export also worked for me.