hCaptcha / react-hcaptcha

hCaptcha Component Library for ReactJS and Preact
MIT License
371 stars 42 forks source link

onLoad not called when mounting component in next page after client side navigation #200

Open Mohitkumar6122 opened 1 year ago

Mohitkumar6122 commented 1 year ago

I have 2 pages a and b. I want to call a secure API and handling the bot request with the help of invisible hcaptcha and verify it on middleware. I am using it on mount of component of page a but when i am navigating to page b and again navigate back to a. onload is not running again. Any ideas what are the scenarios in which onload runs? and how to prevent this ?

Page a -> Page b -> Page a captcha working captcha not generating using onload prop

e271828- commented 1 year ago

Onload only fires once for a SPA, so this is expected behavior. What are you trying to do in onload?

Mohitkumar6122 commented 1 year ago

I have token generation logic in onload, diffrent for both pages a and b but the onload is firing only for page a (which is loaded earlier) not for page b (after page a).

zoryana94 commented 1 year ago

Hi @Mohitkumar6122!

Thank you for reaching to us! As @e271828- mentioned, , that's the expected behavior! The onLoad is triggered when the hCaptcha script is loaded. You can find more info here. Right now, we don't have the prop that would be triggered when the new instance of hCaptcha is ready for use.

Please try calling hCaptcha execute method inside the useEffect instead. Here is the example code with the additional isReady check:

useEffect(() => {
    let timeoutId;

    if (captchaRef.current?.isReady()) {
      executeCaptcha();
    } else {
      timeoutId = setTimeout(() => {
        executeCaptcha();
      }, 500);
    }

    return () => {
      if (timeoutId) {
        clearTimeout(timeoutId);
      }
    };
  }, []);

  const executeCaptcha = async () => {
    try {
      const captchaResponse = await captchaRef.current.execute({
        async: true
      });
      const { response: responseToken } = captchaResponse;

      console.log('Verified asynchronously: ', responseToken);

      setToken(responseToken);
    } catch (error) {
      console.log(error);
    }
  };

Please let us know if it works for you.

Best Regards, hCaptcha Dev Team

Mohitkumar6122 commented 1 year ago

@zoryana94 Thanks for providing a workaround, but this is the same method as I'm using till now. Just one more doubt if I remove the 'hcaptca-api-script' from the head tag when the page a unmounts.

useEffect(() => {

    // MY CUSTOM LOGIC

    return () => {
      const apiScript = document.getElementById("hcaptcha-api-script-id");
      apiScript && document.head.removeChild(apiScript as HTMLUnknownElement);
    };
  }, []);

But after navigating to page b, ideally it should load a new script and onLoad should work ? But its not working. Any ideas what could be the issue here ?

zoryana94 commented 1 year ago

Hi,

@Mohitkumar6122, we check if the script content is loaded, not if the script element is present that should be the reason

however, what do you mean by "this is the same method as I'm using till now"? you probably use the executeCaptcha method, but what about the following part:

useEffect(() => {
    let timeoutId;

    if (captchaRef.current?.isReady()) {
      executeCaptcha();
    } else {
      timeoutId = setTimeout(() => {
        executeCaptcha();
      }, 500);
    }

    return () => {
      if (timeoutId) {
        clearTimeout(timeoutId);
      }
    };
  }, []);

So, my suggestion is that you call the executeCaptcha as in the example above, not in the onLoad.

If that is still not working for you, could you please send us some details of how you've added hCaptcha into your app? or even maybe some test app

Best Regards, hCaptcha Dev Team

Mohitkumar6122 commented 1 year ago

@zoryana94 Thanks for the clarification. AS for the second part, Since I'm calling an API, I cannot use the execute method in the useEffect or else it will trigger unnecessary API call every time component rerenders, As of now I'm using the useEffect for the token generation and storing some variable in the sessionStorage to trigger API call only once, But let me try the method you mentioned, I think it'll work.

Mohitkumar6122 commented 1 year ago

Hi,

@Mohitkumar6122, we check if the script content is loaded, not if the script element is present that should be the reason

however, what do you mean by "this is the same method as I'm using till now"? you probably use the executeCaptcha method, but what about the following part:

useEffect(() => {
    let timeoutId;

    if (captchaRef.current?.isReady()) {
      executeCaptcha();
    } else {
      timeoutId = setTimeout(() => {
        executeCaptcha();
      }, 500);
    }

    return () => {
      if (timeoutId) {
        clearTimeout(timeoutId);
      }
    };
  }, []);

So, my suggestion is that you call the executeCaptcha as in the example above, not in the onLoad.

If that is still not working for you, could you please send us some details of how you've added hCaptcha into your app? or even maybe some test app

Best Regards, hCaptcha Dev Team

Also How do you check if the script content is loaded ? Are there any signatures that Hcaptcha leaves on the DOM or window object after I remove the script, Ideally it should remove all the dependencies of Hcaptcha ?

zoryana94 commented 1 year ago

Hi,

@Mohitkumar6122 as far as I remember we're checking for the hcaptcha in the frame window. However, I'd not recommend trying to go with such the workarounds like deleting it or so. It should be easier to align the current implementation.

So, the executeCaptcha I put above is also only for the token generation. Please make sure to use it with the empty dependencies array, so it's called just once. In this case, it should work similar to the behavior you described as desired originally: with onLoad on mounting. If you make any non-related API call, please put it into the different useEffect.

Also, it might be useful if you send some code implementation example, so that we could propose the possible solution.

Best Regards, hCaptcha Dev Team