jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.88k stars 2.79k forks source link

Field validator submits validation result after field is unmounted and unregistered #2385

Open dennisoelkers opened 4 years ago

dennisoelkers commented 4 years ago

🐛 Bug report

First of all: Thanks a lot for formik and for your continued effort to provide a high quality open source library and support!

Current Behavior

I am creating a kinda "polymorphic" form (say it's namespace is "something") which allows toggling its type using a select and rendering other fields (called subfields) conditionally based on the selected type, e.g. there is a type A with field "something.foo" and type B with field "something.bar". When changing the type, values for the subfields are reset by setting a value for the complete "something" key.

Subfields have validate functions attached to them. When changing the type e.g. from type B to type A, the validator of field "bar" is run before field "bar" is unmounted, leaving over a validation error for "something.bar".

From debugging this, I could see that the execution order of the different lifecycles is like this:

  1. Setting new type
  2. Old field running validation (with low priority)
  3. Old field unmounting: unregistering field (with high priority)
  4. Field validation from 2. submitting results

Maybe I am doing something wrong? The code sandbox example is a simplified version of what I am doing. Interestingly, it works with formik 1.x. Any help, especially if it shows that it's just me being dumb is highly appreciated!

Expected behavior

It should be able to unregister a field before unmounting it and prevent further validations to run afterwards.

Reproducible example

I created a sandbox here. To see the issue, do the following:

  1. Change type to "B"
  2. See in Formik stats at the bottom that errors contains an entry for foo, although no foo field exists anymore.

Suggested solution(s)

Additional context

Your environment

Software Version(s)
Formik 2.1.4
React 16.13.1
TypeScript
Browser
npm/Yarn
Operating System
rus-yurchenko commented 4 years ago

I faced the same issue. Form validation on that field runs after the field was unmounted even when trying reset errors on field unmount.

dennisoelkers commented 4 years ago

Hey @jaredpalmer, do you have any advice for us? Maybe we are doing something wrong. Is it even supposed to work with fields mounting/unmounting & registering/unregistering having custom validation functions attached to them?

rus-yurchenko commented 4 years ago

@dennisoelkers I've fixed my issue with, probably, some workaround. In my case, I just need to drop unmounted field value from formik state and don't fire validation on an unmounted field ( this is the sense of issue).

I've combined two approaches to accomplish it. I'm not sure about the unregistering field, instead, I've used formik.setFieldValue() with disabled validation on field unmount with a conjunction of Yarn conditional validation (https://github.com/jquense/yup#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema).

I tried to do the same with field validation function, but it doesn't fire on the unmounted fields.

Therefore, when the field gets unmounted, yarn conditional validation checks my trigger field value and just doesn't run validation on it.

Hope that would help you.

dennisoelkers commented 4 years ago

Hey @rusyurchenko!

Thanks for the update. How do you use setFieldValue with disabled validation on field unmount? Do you have a code snippet for me by any chance?

rus-yurchenko commented 4 years ago

@dennisoelkers I have my own custom inputs, therefore, I have full control over it. SImple call setFieldValue on useEffect clear function.

  const formik = useFormikContext();
  useEffect(
    () => () => {
      formik.setFieldValue(name, undefined, false);
    },
    []
  );
dennisoelkers commented 4 years ago

Thanks, @rusyurchenko!

That does not really solve it for me. When setFieldValue is called, the field validator has already been kicked off. The main issue is that the validator's result is submitted after anything triggered explicitly during unmounting.

rus-yurchenko commented 4 years ago

That's why I've used a Yup conditional validation. It didn't fire validation after the field was unmounted. As I said previously it’s just a workaround OR desired behavior by design.

dennisoelkers commented 4 years ago

I am maybe a bit slow here, but how is yarn related? I think you mean yup? I have not used yup before, I think it does mean though that a validation schema is defined on the parent Formik component, which knows about the fields? I am using field level validation because the parent component does not know about the structure of the child components rendering Fields.

How does your yup schema look like, that fixes validation running after unmounting the field?

kostiantyn-solianyk commented 4 years ago

any updates?

dvakatsiienko commented 4 years ago

Wow, curious that more people faced the same problem! So +1 on this.

I am building a stepper and need to conditionally add field to formik state or or remove them because if not after user passes a stepper form values includes a lot of trash field values that are left from branches.

So when inside of a from a different branch is selected, and another set of fields is rendered instead of previous set of fields, the cleanup of old fields in formik state is required.

Tested out internal form.registerField and form.unregisterField methods but they are not giving me anything yet.

UPDATE: found that method form.setValues allows you to set entirely new form data shape.

const prevFormValues = {
    name: 'Walter',
    surname: 'White'
}

...
form.setValues({
    name: 'Jack'
})
...

// nextFormValues will be → { name: 'Jack' } 

Hope it helps someone.

gornypiotr commented 2 years ago

My problem is kind of similar: I have a Formik-managed form, wrapped around a Material UI Stepper. The fields are split onto 3 steps. I want the validation kick off, either when I leave a field (on blur) or when the save button is pressed. That's normal in Formik and is good. Unfortunately what happens is this: when I move between the steps, the fields, while being unmount send onBlur, so all fields, from all steps get the touched state and get validated. This is confusing for the user, since they move to the next step and it's already validated and full of errors...

Is there a way to prevent this behaviour?

russtuck91 commented 2 years ago

+1 on this issue, I'm facing the same thing. I have a dynamic form and I use validate functions on each Field component. (I do not use Yup validation schemas.) When Fields are unmounted, their errors stay behind.

Works ok with Formik v1, but this bug prevents me from upgrading to v2