react-google-recaptcha icon indicating copy to clipboard operation
react-google-recaptcha copied to clipboard

Resetting captcha with redux-form

Open pasichnyk opened this issue 7 years ago • 3 comments

How do I reset captcha if I'm using redux-form ?

export const FormCaptcha = ({ input }) => {}
        <ReCAPTCHA {...input}
              sitekey="xxxxxxxxxxxxx"
        />
<Field
        name="g-recaptcha-response"
        component={FormCaptcha}
/>

pasichnyk avatar Apr 24 '18 14:04 pasichnyk

class Captcha extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      shouldReset: false
    }
    this.component = React.createRef()
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    return { shouldReset: nextProps.reset }
  }

  render() {
    return (
        <ReCAPTCHA
          ref="recaptcha"
          sitekey={this.props.sitekey}
          onChange={value => this.props.onChange(value)}
          ref={this.component}
        />
    )
  }

  componentDidUpdate() {  
      if (this.state.shouldReset) this.component.current.reset()
  }
}
<Captcha
            sitekey="XXX"
            reset={this.state.shouldCaptchaReset}
            onChange={value => {
              this.setState({ captcha: value, shouldCaptchaReset: false })
            }}
          />

I use this and it works, but I don't know if this is a good thing : every time captcha send a value to the form (which handle captcha), should Captcha reset is set to false. But when I send the form, if an error occurs (another field is missing for exemple), I set the shouldCaptchaReset to false, so the captcha is reseting and will need another ok from google, otherwise it will reset at each render

azopyros avatar May 29 '18 09:05 azopyros

You might need to include the withRef option on the Field component, which then allows you access to the getRenderedComponent method, so you can reach the component that renders your recaptcha component. You can then use refs to call the reset method from a parent component (so long as it's class-based).

See redux form docs

e.g.

<Field
    component={RenderRecaptcha}
    classNames="justify-content-end"
    name="recaptcha"
    ref={el => {
    this.captcha = el;
    }}
    withRef
/>

Then where you want to reset it, you can write:

const captcha = this.captcha;
captcha.getRenderedComponent().reset();

In my app I'm reaching through a couple of component levels, so I have a basic reset method on the parent of the recaptcha component using refs (as it's decorated with validation bumf to connect with redux form's various 'valid', 'invalid', 'touched' props etc.), which is rendered by the redux form Field component prop. I then trigger that reset handler through the Field using the above refs process.

csleary avatar Jul 26 '18 20:07 csleary

another solution that works for me:

import React from 'react'
import Label from 'Shared/components/FormElements/Label/Label'
import ReCAPTCHA from 'react-google-recaptcha'
import withTranslations from 'Shared/hoc/withTranslations'

class Captcha extends React.Component {
  captcha = {}

  componentWillReceiveProps (nextProps) {
    if (this.props.meta.submitting && !nextProps.meta.submitting && !nextProps.submitFailed) {
      this.captcha.reset()
    }
  }
  render () {
    const { input: { onChange }, meta: { error }, t } = this.props
    return (
      <div className={`captcha-container ${error && 'captcha-container__error'}`}>
        {error && <Label>{t(error.key)}</Label>}
        <ReCAPTCHA
          sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_API_KEY}
          onChange={onChange}
          ref={el => { this.captcha = el }}
        />
      </div>
    )
  }
}

export default withTranslations(Captcha)

michalpokojski avatar Sep 05 '18 06:09 michalpokojski