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

the form stays in validating state in a wizard when field is unregistred #1018

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

iamdey
Copy link

@iamdey iamdey commented Dec 19, 2022

Not yet a contribution sorry, it's only a failing test that should not.

With 2 fields in a «wizard» form, if one has an async validation and the other doesn't, the form stays in validating state

This PR adds 2 tests:

  • 1st with mixed validation sync & async field
  • 2nd with only async validation

Rq: I unsuccessfully tried to reproduce this issue in final-form directly.
It seems related to runFieldLevelValidation but for now I got lost in the step-by-step debugger

@iamdey
Copy link
Author

iamdey commented Dec 19, 2022

Because I can't always keep fields registered, I found 2 workarounds:

  • use at least one field with async validation but it needs to be slower than React re-render which is obviously not acceptable
  • pause validation while unregistering / registering fields

Reusing the same code as the provided test:

const Test = () => {
      const [hasField, setHasField] = React.useState(true);
      const state = useFormState({ subscription: { validating: true } });
      const form = useForm();

      React.useEffect(() => {
        // required as workaround to make sure validating state is up-to-date 
        form.resumeValidation();
      }, [hasField]);

      return (
        <div>
          {!hasField && (
            <Field
              name="lastname"
              component="input"
              validate={(value) => (value ? undefined : "Required")}
              data-testid="lastname"
            />
          )}
          {hasField && (
            <Field
              name="name"
              component="input"
              validate={async (value) => {
                await timeout(5);
                return value === "erikras" ? "Username taken" : undefined;
              }}
              data-testid="name"
            />
          )}
          <div data-testid="validating">
            {state.validating === true ? "Spinner" : "Not Validating"}
          </div>
          <button
            data-testid="hide"
            onClick={() => {
              // required as workaround to make sure validating state is up-to-date 
              form.pauseValidation();
              setHasField(false);
            }}
          >
            Hide field
          </button>
        </div>
      );
    };

    const { getByTestId, queryByTestId } = render(
      <Form onSubmit={onSubmitMock}>
        {({ handleSubmit }) => (
          <form onSubmit={handleSubmit}>
            <Test />
          </form>
        )}
      </Form>,
    );

@xxleyi
Copy link

xxleyi commented Feb 25, 2023

Rq: I unsuccessfully tried to reproduce this issue in final-form directly.
It seems related to runFieldLevelValidation but for now I got lost in the step-by-step debugger

Yes, in useField hook fields register synchronously in React render, but can only unregister in unmount phase, and final form looks only runFieldLevelValidation in register, which only notify on field level. When we field level async validation, it in fact will affect form state validating.

So, below code can fix this problem, but I dont know if it is good for other part

    if (hasAsyncValidations) {
      var afterPromise = function afterPromise() {
        state.formState.validating--;
        callback();
        // field async validation may affect formState validating
        // so force notifyFormListeners if validating is still 0 after callback finished
        // and lastFormState validating is true
        if (state.formState.validating === 0 && state.lastFormState.validating) {
          notifyFormListeners();
        }
      };
      promise.then(function () {
        if (nextAsyncValidationKey > asyncValidationPromiseKey) {
          return;
        }
        processErrors(true);
      }).then(afterPromise, afterPromise);
    }

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

Successfully merging this pull request may close these issues.

2 participants