I have a react-modal form with a captcha in it. When the user open up the modal the captcha shows up just above the Report Job button. No problems there. but let say the user closes the modal before even submitting or escaped the modal. so in that case when the user opens it again probably the ReCAPTCHA must show up; but it doesnt. And in another case even if the user submits the form and then opens up the modal again in that case too, the ReCAPTCHA doesnt shows up.
But if i refresh the website and then try to open up the modal in that case it works fine. but no one is going to refresh everytime to see the captcha.
"use client";
import { useState, useRef } from "react";
import Modal from "react-modal";
import ReCAPTCHA from "react-google-recaptcha";
Modal.setAppElement("#modal");
export default function ReportJobForm() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [inValidCaptcha, setInValidCaptcha] = useState(true);
const recaptchaRef = useRef<ReCAPTCHA>(null);
const openModal = () => setIsModalOpen(true);
const closeModal = () => {
setIsModalOpen(false);
setInValidCaptcha(true);
console.log("recaptchaRef", recaptchaRef);
if (recaptchaRef.current) {
recaptchaRef.current.reset();
}
};
const [formData, setFormData] = useState({
name: "",
email: "",
reason: "",
message: "",
});
const handleCaptchaOnChange = (value: string | null) => {
console.log("ReCAPTCHA value:", value);
if (value) {
console.log(value);
setInValidCaptcha(false);
}
};
const handleInputChange = (e: any) => {
const { name, value } = e.target;
setFormData((prevFormData) => {
return {
...prevFormData,
[name]: value,
};
});
};
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
closeModal();
recaptchaRef.current?.reset();
console.log(formData);
setInValidCaptcha(true);
};
return (
<>
<button className="cursor-pointer" onClick={() => openModal()}>
Report Job
</button>
<Modal
isOpen={isModalOpen}
onRequestClose={closeModal}
overlayClassName="bg-[rgba(0,0,0,.4)] flex justify-center items-center fixed top-0 left-0 h-screen w-screen"
className="w-3/10 bg-white rounded-xl p-6"
>
<h1 className="text-xl font-bold mb-4">Report job</h1>
<form onSubmit={handleFormSubmit}>
<div className="mb-4">
<label
className="block text-gray-700 font-bold mb-2"
htmlFor="name"
>
Name*
</label>
<input
className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="name"
name="name"
type="text"
value={formData.name}
onChange={handleInputChange}
required
/>
</div>
<div className="mb-4">
<label
className="block text-gray-700 font-bold mb-2"
htmlFor="email"
>
Email*
</label>
<input
className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleInputChange}
required
/>
</div>
<div className="mb-4">
<label
className="block text-gray-700 font-bold mb-2"
htmlFor="reason"
>
Reason
</label>
<div className="relative">
<select
className=" border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="reason"
name="reason"
value={formData.reason}
onChange={handleInputChange}
required
>
<option value="">Please select a reason</option>
<option value="incorrect data">Incorrect data</option>
<option value="job expired">Job expired</option>
<option value="job not found">Job not found</option>
</select>
</div>
</div>
<div className="mb-4">
<label
className="block text-gray-700 font-bold mb-2"
htmlFor="message"
>
Message*
</label>
<textarea
className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="message"
name="message"
value={formData.message}
onChange={handleInputChange}
required
/>
</div>
<ReCAPTCHA
sitekey="Key"
onChange={handleCaptchaOnChange}
ref={recaptchaRef}
/>
<button
disabled={inValidCaptcha}
className="bg-red-500 w-full
hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Report Job
</button>
</form>
</Modal>
</>
);
}
I tried refering to a youtube video for reseting the ReCAPTCHA which it used useRef, the same solution were applied to mine too as you can see in the code. but it didnt work at all. When i tried to console.log
the recaptchaRef.current
it gives me null.
I also tried referring to this stackoverflow questions but no luck. either i am not understanding it right or something wrong with my implementions.
I am a begineer in nextjs and react-google-recaptcha please help.
2
Answers
I think I have found the source of the issue.
Unfortunately, the issue is being caused by the react-modal package that is used. It is being described here https://github.com/reactjs/react-modal/issues/808 . And it seems to be caused by React v18 and strict mode. The problem seems to be when the modal is closed then a reference to it is still being held somewhere in memory.
It is described in a bit more detail by the post here https://github.com/reactjs/react-modal/issues/808#issuecomment-1446289359 . However I believe that Google Recaptcha thinks that the previous modal element still exists because it is being held in memory rather than being garbage collected.
So how React v18, when used in strict mode, handles garbage collection of objects. There is still a reference to the unmounted object being kept in memory somewhere and it is confusing Recaptcha.
When I removed
reactStrictMode: true
, from the/next.config.js
file, and restarted the development server environment everything seems to work.But as you know we don't want that.so without removing it just run
npm run build && npm start
in build mode it will work fine.It was a very strange issue. I have not seen anything like it before which made debugging difficult.
The fact that you have a
null
ref showing up is because the Modal is not mounted, so the reference do not exist at all maybe be you should dig into that and change the priority (reset before closing the modal)So for example :