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

Async validator on unregister set validating to true #780

Closed
rvsia opened this issue Apr 17, 2020 · 10 comments · Fixed by final-form/final-form#473
Closed

Async validator on unregister set validating to true #780

rvsia opened this issue Apr 17, 2020 · 10 comments · Fixed by final-form/final-form#473

Comments

@rvsia
Copy link

rvsia commented Apr 17, 2020

Are you submitting a bug report or a feature request?

bug report

What is the current behavior?

On Wizard page we have a step with a field, that has async validator.

After going to next step, the field is unregistered and a new field is registered with a not-async validator.

However, the validating is set to true when entering the step and after it's changed to false, the FormSpy component is not updated and re-rendered. So, the next button remains disabled.

This behavior was introduced in #766

What is the expected behavior?

When validating is set to false, re-render FormSpy

Sandbox Link

https://codesandbox.io/s/react-final-form-simple-example-0rc9g?fontsize=14&hidenavigation=1&theme=dark

Code

### code

import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field, FormSpy } from "react-final-form";

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const asyncValidator = () => new Promise(res => setTimeout(() => res(), 1000));

const requiredValidator = value => (value ? "" : "required");

class App extends React.Component {
  state = { mounted: false };
  componentDidMount() {
    this.setState({ mounted: true, step: 0 });
  }
  render() {
    return (
      <Styles>
        <Form
          onSubmit={
            this.state.mounted
              ? async values => {
                  await sleep(300);
                  window.alert(JSON.stringify(values, 0, 2));
                }
              : () =>
                  window.alert(
                    "You are incorrectly calling an outdated onSubmit"
                  )
          }
          render={({ handleSubmit, reset, submitting, pristine, values }) => (
            <form onSubmit={handleSubmit}>
              <div>
                {this.state.step === 0 && (
                  <React.Fragment>
                    <label>First Name</label>
                    <Field
                      name="firstName"
                      component="input"
                      type="text"
                      placeholder="First Name"
                      validate={asyncValidator}
                    />
                  </React.Fragment>
                )}
                {this.state.step === 1 && (
                  <React.Fragment>
                    <label>Password Requiered</label>
                    <Field
                      name="password"
                      component="input"
                      type="text"
                      placeholder="12345"
                      validate={requiredValidator}
                    />
                  </React.Fragment>
                )}
              </div>
              <FormSpy>
                {({ validating, submitting, pristine, valid }) => (
                  <div className="buttons">
                    {this.state.step === 0 && (
                      <button
                        type="button"
                        onClick={() => this.setState({ step: 1 })}
                        disabled={validating || !valid || pristine}
                      >
                        Next
                      </button>
                    )}
                    {this.state.step === 1 && (
                      <button
                        type="submit"
                        disabled={validating || submitting || pristine}
                      >
                        Submit
                      </button>
                    )}
                    <button
                      type="button"
                      onClick={reset}
                      disabled={submitting || pristine}
                    >
                      Reset
                    </button>
                    <pre>
                      {JSON.stringify({ values, validating, valid }, 0, 2)}
                    </pre>
                  </div>
                )}
              </FormSpy>
            </form>
          )}
        />
      </Styles>
    );
  }
}
render(<App />, document.getElementById("root"));

What's your environment?

    "final-form": "^4.19.1",
    "final-form-arrays": "^3.0.2",
    "final-form-focus": "^1.1.2",
    "react-final-form": "^6.4.0",
    "react-final-form-arrays": "^3.1.1"

Node v10.16.0

Issue
data-driven-forms/react-forms#431

cc @Hyperkid123

@rvsia
Copy link
Author

rvsia commented Apr 17, 2020

This error appears when entering the step with the async validate:

Uncaught (in promise) TypeError: Cannot read property 'active' of undefined
    at publishFieldState (final-form.es.js:181)
    at notifyField (final-form.es.js:734)
    at notifyFieldListeners (final-form.es.js:745)
    at notify (final-form.es.js:1065)
    at afterPromise (final-form.es.js:707)

@Chrisdo82
Copy link

I have the same issue

@rvsia
Copy link
Author

rvsia commented May 15, 2020

@erikras any thoughts?

@ArnoZx
Copy link

ArnoZx commented Jun 22, 2020

I have the same issue either

@Chrisdo82
Copy link

@erikras is there a workaround to avoid this behaviour?

@rvsia
Copy link
Author

rvsia commented Sep 24, 2020

@Chrisdo82

Not a great workaround (actually it's a terrible one 😃), but after the unregistering the field with a async validation we trigger form.change('nonsense', 'nonsense') in a timeout to rerender the form. (Just be sure you don't send the value in your submit.)

@bartek2341
Copy link

Any new solutions ?

@kgregory
Copy link

Found a decent workaround in @alanpoulain's pull request

  const form = useForm();
  // Pause the validation while the Final Form field is registered to prevent a form state desynchronization bug when using async validators.
  // Since the field is not registered directly because of the introspection, Final Form is using the previous form state (without the field) when notifying after the async validation done during the registration.
  // See also https://github.com/final-form/react-final-form/issues/780.
  form.pauseValidation();
  useEffect(() => {
    form.resumeValidation();
  }, [form]);

@xxleyi
Copy link

xxleyi commented Feb 25, 2023

Found a decent workaround in @alanpoulain's pull request

  const form = useForm();
  // Pause the validation while the Final Form field is registered to prevent a form state desynchronization bug when using async validators.
  // Since the field is not registered directly because of the introspection, Final Form is using the previous form state (without the field) when notifying after the async validation done during the registration.
  // See also https://github.com/final-form/react-final-form/issues/780.
  form.pauseValidation();
  useEffect(() => {
    form.resumeValidation();
  }, [form]);

not work in normal component

@xxleyi
Copy link

xxleyi commented Feb 25, 2023

@Chrisdo82

Not a great workaround (actually it's a terrible one 😃), but after the unregistering the field with a async validation we trigger form.change('nonsense', 'nonsense') in a timeout to rerender the form. (Just be sure you don't send the value in your submit.)

Inspired by this workaround, is using such a workaround:

// use change to trigger final form to run validation again
function forceValidate(form: FormApi<any>) {
  const randomName = 'a' + Math.random().toString(36).slice(2);
  form.change(randomName, 'a');
  form.change(randomName, undefined);
}

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 2, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants