Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Mohitkumar6122 opened this issue Sep 16, 2023 · 8 comments
Assignees

Comments

@Mohitkumar6122
Copy link

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-
Copy link
Contributor

e271828- commented Sep 16, 2023

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

@Mohitkumar6122
Copy link
Author

Mohitkumar6122 commented Sep 16, 2023

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
Copy link
Contributor

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
Copy link
Author

Mohitkumar6122 commented Sep 16, 2023

@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
Copy link
Contributor

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
Copy link
Author

@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
Copy link
Author

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
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants