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

onBlur validation cancels out onSubmit #786

Closed
ghost opened this issue Jul 24, 2018 · 7 comments
Closed

onBlur validation cancels out onSubmit #786

ghost opened this issue Jul 24, 2018 · 7 comments
Labels

Comments

@ghost
Copy link

ghost commented Jul 24, 2018

Summary

When onChange={handleChange} is used in conjunction with onBlur={handleBlur}, the onSubmit={handleSubmit} event is not triggered until the second attempt. After some digging, I believe this is because the setState(...) calls in handleBlur are canceling out the onClick or onSubmit events. Having fiddled around with the internals, I've noted that without the setState(...) inside of handleBlur, this does not happen.

Current Behavior

I'm using the following code:

const SignUpForm = ({ onSubmit }) => (
  <Formik
    initialValues={{
      email: "",
      password: ""
    }}
    validationSchema={Yup.object().shape({
      email: emailValidator,
      password: passwordValidator
    })}
    onSubmit={onSubmit}
    render={({
      values,
      errors,
      touched,
      handleChange,
      handleBlur,
      handleSubmit,
      isValid,
      isSubmitting
    }) => {
      const emailError = touched.email && errors.email;
      const passwordError = touched.password && errors.password;

      return (
        <SignUpFormInternal
          isValid={isValid}
          email={values.email}
          emailHelperText={emailError && errors.email}
          emailError={Boolean(emailError)}
          onEmailChange={handleChange("email")}
          onEmailBlur={handleBlur("email")}
          password={values.password}
          passwordHelperText={passwordError && errors.password}
          passwordError={Boolean(passwordError)}
          onPasswordChange={handleChange("password")}
          onPasswordBlur={handleBlur("password")}
          onSubmit={handleSubmit}
          isSubmitting={isSubmitting}
        />
      );
    }}
  />
);

Steps to Reproduce

  • Create a form with a single text field and a submit button
  • Apply onBlur={handleBlur}, onChange={handleChange}, and onSubmit={handleSubmit}
  • Enter a value into the text field and click the submit button
  • At this point, the onSubmit callback will not have been fired
  • Click the submit button once again
  • At this point, the onSubmit callback will have been fired

Expected behavior

My expectation is that, with the provided configuration, the onSubmit callback would be fired the first time around.

Suggested solution(s)

I have had luck in resolving this issue by doing the following within handleBlur:

setTimeout(() => {
  this.setState(prevState => ({
    touched: setIn(prevState.touched, field, true),
  }));

  if (this.props.validateOnBlur) {
    this.runValidations(this.state.values);
  }
}, 0);

Effectively, this forces the state change side-effects from handleBlur to be pushed back onto the event queue, thus allowing it to be prioritized behind handleSubmit. This definitely seems like a sketchy way to solve this problem, however.

Additional context

CodeSandbox Link:


  • Formik Version:
  • React Version: 16.4
  • TypeScript Version: N/A
  • Browser and Version: Chrome 67
  • OS: OSX Sierra
  • Node Version: 8.11.3
  • Package Manager and Version: npm 5.6.0
@ghost
Copy link
Author

ghost commented Jul 25, 2018

Related React issues:
facebook/react#2291
facebook/react#4210

These have been closed but reading through the comments there is no true resolution. But there has got to be a clean workaround.

@ghost
Copy link
Author

ghost commented Jul 25, 2018

Looks like this could be a batching issue 🤔 React pre-16 only batches events, and handleBlur is forced to persist its event. This could mean that for trivial cases upgrading to React 16 would be sufficient, but this would likely be a race condition.

@stale
Copy link

stale bot commented Sep 23, 2018

Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.

@stale stale bot added the stale label Sep 23, 2018
@stale
Copy link

stale bot commented Sep 30, 2018

ProBot automatically closed this due to inactivity. Holler if this is a mistake, and we'll re-open it.

@stale stale bot closed this as completed Sep 30, 2018
@ivoreis
Copy link

ivoreis commented Jan 13, 2019

@enagy-earnup any luck with this?

@ghost
Copy link
Author

ghost commented Jan 14, 2019

@ivoreis yes! I was going about this all wrong, and it showed 😄 I was trying to put the submit on the button as opposed to putting it on the form, and that was causing a number of issues. I don't think my example really shows the code at fault. In short: use standard html best practices 😅

@brujorge
Copy link

brujorge commented Oct 4, 2019

I fixed this issue by using React.memo to create a component for the submit button and preventing the component to re-render after an element loses focus and triggers the onBlur validation since the props of the submit button won't change. Does anyone have any other workaround?

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

No branches or pull requests

2 participants