react-final-form
react-final-form copied to clipboard
the form stays in `validating` state in a wizard when field is unregistred
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
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>,
);
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);
}