2
votes

I'm trying to make invisible react-google-recaptcha, Formik and yup to work together. The documentation says we should call recaptchaRef.current.execute() on form submission, but if we use Formik and yup together, it will trigger the submission logic only after all fields passed the validation schema.

Basically, we need to call the execute method, update the recaptcha value and submit the form with the same trigger event. My problem is exactly that: I'm having to use two events (one for the execute method and update the recaptcha + one to submit the form).

Check this sandbox: https://codesandbox.io/s/keen-mahavira-ont8z?file=/src/App.js

As you can see, the form is submitted only with the second click in the submit button...

1

1 Answers

3
votes

With Formik, there are some ways to do background work for your form's. This basically could be achieved with handleChange or handleBlur props being passed to the form component.

For instance, I am sure you would have other inputs in your form elements and not just a captcha (if it's just a captcha in the form, then do let me know! - this can also be solved)

So when you have other elements, you can ensure to use some of the Formik's API to handle the automatic trigger:

As I see, there are a lot of ways to handle this through their API's: https://formik.org/docs/api/formik

The way I tried to achieve it is by adding a listener for onBlur on all fields and then checking if reCaptcha value is present or not. Based on that I trigger the execute the captcha and ensure to set the submitting value as true:

const handleBlur = (e) => {
  console.log("$$$$", props.isSubmitting);
  if (!props.values.recaptcha) {
    this._reCaptchaRef.current.execute();
    props.setSubmitting(true);
  }
  props.handleBlur(e);
};

Here is the CodeSandbox Link: https://codesandbox.io/s/silly-saha-qq7hg?file=/src/App.js

This shows the working model of handling onBlur of a field and triggering it in the background. If you notice, you can also disable and enable the submit button using isSubmitting and setSubmitting.

Also setting validateOnChange={false} and validateOnBlur={false}, because there is no need to validate on change or blur for captcha.

Pasting code here just in case for you to glance:

import React, { Component, createRef } from "react";

import ReCAPTCHA from "react-google-recaptcha";
import { Formik } from "formik";
import * as yup from "yup";

const TEST_SITE_KEY = "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI";

export default class MyForm extends Component {
  constructor(props) {
    super(props);
    this._validationSchema = yup.object().shape({
      recaptcha: yup.string().required(),
      name: yup.string().required(),
      address: yup.string().required()
    });
    this._initialValues = { recaptcha: "", name: "", address: "" };
    this._reCaptchaRef = createRef();
  }

  render() {
    return (
      <Formik
        validationSchema={this._validationSchema}
        initialValues={this._initialValues}
        validateOnChange={false}
        validateOnBlur={false}
        onSubmit={(values) => console.log(values)}
      >
        {(props) => {
          const handleBlur = (e) => {
            console.log("$$$$", props.isSubmitting);
            if (!props.values.recaptcha) {
              this._reCaptchaRef.current.execute();
              props.setSubmitting(true);
            }
            props.handleBlur(e);
          };

          return (
            <form onSubmit={props.handleSubmit}>
              <label>Name: </label>
              <input
                type="text"
                onChange={props.handleChange}
                value={props.values.name}
                name="name"
                onBlur={handleBlur}
              />
              <label>Address: </label>
              <input
                type="text"
                onChange={props.handleChange}
                value={props.values.address}
                name="address"
                onBlur={handleBlur}
              />
              <ReCAPTCHA
                ref={this._reCaptchaRef}
                sitekey={TEST_SITE_KEY}
                onChange={(value) => {
                  console.log("$$$$", props.isSubmitting, value);
                  props.setFieldValue("recaptcha", value);
                  props.setSubmitting(false);
                }}
                size="invisible"
              />
              <button type="submit" disabled={props.isSubmitting}>
                SUBMIT
              </button>

              {props.errors.name && <div>{props.errors.name}</div>}
            </form>
          );
        }}
      </Formik>
    );
  }
}