Closed teolaz closed 1 day ago
Just a small update, if it could help...
I've tried to append the "statechange" event handler only on "load" event like this
import { useField } from "formik";
import React, { useEffect, useRef } from "react";
import { getConfig } from "../../../utils/commonMethodsFrontend";
export default function Altcha() {
const [field, meta, helpers] = useField("altcha");
// const { value } = meta;
const { setValue } = helpers;
const widgetRef = useRef(null);
useEffect(() => {
console.log("entering useEffect");
const handleStateChange = (ev) => {
console.log(ev);
if (ev.detail) {
// state can be: unverified, verifying, verified, error
if (ev.detail.state === "verified") {
// payload contains base64 encoded data for the server
setValue(ev.detail.payload);
console.log("set payload");
} else {
setValue("");
console.log("set empty");
}
}
};
const current = widgetRef.current;
const handleLoad = () => {
console.log("Widget fully loaded");
current.addEventListener("statechange", handleStateChange);
};
if (current) {
console.log("added load event listener");
current.addEventListener("load", handleLoad);
}
return () => {
if (current) {
console.log("removing event listeners...");
current.removeEventListener("load", handleLoad);
current.removeEventListener("statechange", handleStateChange);
}
};
}, [setValue]);
return (
<div className="field-container">
<div className="info">Accept the captcha to continue*</div>
<altcha-widget
ref={widgetRef}
challengeurl={getConfig("netlify_functions_url") + "/altcha-challenge"}
/>
{meta.error && meta.touched ? (
<div className="error">
<span>{meta.error}</span>
</div>
) : null}
</div>
);
}
but nothing changes, the widget launch events only when going back and forth between pages.
Hi, I don't see any import('altcha')
in your code. When attaching event listeners, the widget must be loaded to make it work, so make sure, the altcha script is imported before calling addEventListener. Adding import('altcha')
should do it.
Hi @ovx , yeah that was because I (wrongly) imported it in the parent component(s), I added the line in my component but nothing changed.
Unfortunately this didn't change problem.
I read this post on stack overflow and made some tests. On a local env, the code below works as per screenshots. Instead on Netlify it doesn't, or better, it works only after the second time you enter into contact page. Seems that caching the resource, even when you reload the page, the browser make the process work! The only thing I have in mind is having problems with timing... Maybe my local env is slower so it has got enough time to append the hanlder?
import { useField } from "formik";
import React, { useEffect, useRef } from "react";
import { getConfig } from "../../../utils/commonMethodsFrontend";
if (global.window) {
// Need to import like this as this shouldn't be bundled by webpack and Gatsby SSR
import("altcha");
}
export default function Altcha() {
const [field, meta, helpers] = useField("altcha");
const { setValue } = helpers;
// Reference to the widget
const widgetRef = useRef(null);
useEffect(() => {
const handleStateChange = (ev) => {
console.log("statechange handler triggered");
if (ev.detail) {
if (ev.detail.state === "verified") {
setValue(ev.detail.payload);
} else {
setValue("");
}
}
};
const addWidgetListener = () => {
// Ensure the widget is loaded and available, then add the event listener
if (widgetRef.current) {
console.log("adding statechange event listener on widget");
widgetRef.current.addEventListener("statechange", handleStateChange);
}
};
console.log("document.readyState = " + document.readyState);
// If the document is fully loaded, attach the listener immediately
if (document.readyState === "complete") {
console.log('document.readyState is "complete", adding widget listener');
addWidgetListener();
} else {
// Otherwise, wait for the document to load before attaching the listener
console.log(
'document.readyState is not "complete", adding widget listener on window load'
);
window.addEventListener("load", addWidgetListener);
}
// Cleanup function to remove the event listeners
return () => {
if (widgetRef.current) {
widgetRef.current.removeEventListener("statechange", handleStateChange);
}
window.removeEventListener("load", addWidgetListener);
};
}, [setValue]);
return (
<div className="field-container">
<div className="info">Accept the captcha to continue*</div>
{/* Static widget directly in the JSX */}
<altcha-widget
ref={widgetRef}
challengeurl={`${getConfig("netlify_functions_url")}/altcha-challenge`}
></altcha-widget>
{meta.error && meta.touched && (
<div className="error">
<span>{meta.error}</span>
</div>
)}
</div>
);
}
Hi, as I wrote earlier, it's important to add the listener after the script loaded. Because you're using SSR, you've added import('altcha')
which is asynchronous and thus the code after the import is executed before the altcha script loads. The react example (https://github.com/altcha-org/altcha-starter-react-ts) uses synchronous import 'altcha'
(note the missing parentheses) which guarantees, that the altcha script loads before the following code.
To fix your SSR code, I recommend adding the listener after the import resolves instead of listening for the global 'load' event, something like this:
if (widgetRef.current && global.window) {
import("altcha").then(() => {
widgetRef.current.addEventListener('statechange', handleStateChange)
});
return () => widgetRef.current.removeEventListener('statechange', handleStateChange)
}
Hi @ovx, it seems that my beautiful low knowledge in modern js code brought me to lose 3 days of life for 2 parethesis. How much I love being a programmer!
I'm testing it now and it seems to work properly with your mod.
I'm asking a big favour for goofy people like me, if you have space on documentation could you please wrote what you explained specifying the behaviour for SSR?
More detailed info about the issue added to the documentation: https://altcha.org/docs/troubleshooting/#event-listeners-dont-work
Hi there! I'm having an issue that I'm struggling to solve, I've lost many hours and I'd need a hand understanding if there's something that I'm missing of the whole... I'm under GatsbyJS, and using Formik to handle a contact form in React, and I created a custom Field for Altcha that I use for form validation. Here the code of my field(including some test console.log), code developed from your React example available in online documentation:
For what I know, I only instance an object of such component when I'm loading the form page, so I'm not passing it through any function that modifies the instance. The only doubt I have is for that
const [field, meta, helpers] = useField("altcha");
that is giving me the possibility to set field value directly from within this component and not by passing from a parent component. The field value is saved on a virtual object of values that Formik keep for validation, so "altcha" is the name of the saved element to be checked in Formik.The control lifecycle works correctly both in frontend and backend, but there's a strange behaviour that I cannot explain, I also tried to switch from 0.4.3 to new 1.1.0 to see if the problem changed but it seems to be worse than before. Somehow the first time the React DOM loads, the event handler doesn't work. If you press on Altcha verification checkbox, no console.log is launched. Instead, if you go back and enter contact page again and verify with Altcha, you can see in debug console the event handler worked correctly.
You can check the strange behaviour here.
Do you have any idea about the possible problem here? Thank you