jaredpalmer / formik

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

[v2] isValid=true on mount, even though initialValues are invalid and validateOnMount=true #1950

Open martaver opened 4 years ago

martaver commented 4 years ago

🐛 Bug report

Current Behavior

isValid is true once the form mounts, even though the validationSchema invalidates initialValues and the form is untouched.

Expected behavior

isValid prop to reflect the actual validity of the form at all times. Due to initialErrors, validating on mount automatically may be undesirable, which is understandable. So in this case, explicitly setting validateOnMount=true should definitely ensure isValid prop's value on mount is correct.

Reproducible example

Using Formik 2.0.3, an invalid form's isValid prop is true on mount. validateOnMount props doesn't seem to make a difference.

https://codesandbox.io/s/formik-example-zehlg

Suggested solution(s)

Additional context

N/A

Your environment

Software Version(s)
Formik 2.0.3
React 16.11.0
TypeScript 3.6.4
Browser Chrome 77.0.3865.120
npm/Yarn yarn 1.19.0
Operating System macos 10.14.6 (18G103)
nperrier commented 4 years ago

I'm running into this issue too. I have validateOnMount set but I don't see any of my Field validation functions running and isValid is true.

nperrier commented 4 years ago

I don't see where in the source code that validationOnMount is referenced:

https://github.com/jaredpalmer/formik/search?q=validateOnMount&unscoped_q=validateOnMount

It looks like it was never implemented? If that's the case, remove it from the docs.

hannupekka commented 4 years ago

I too had problems with validateOnMount and noticed this: https://github.com/jaredpalmer/formik/blob/master/src/Formik.tsx#L350

As a workaround, I have this hook in my form to do the validation on mount:

useEffect(() => {
  (() => validateForm())();
}, []);

Which is basically what Formik would do under the hood.

jonathanwelton commented 4 years ago

Does anyone know why validateOnMount was commented out and when it will be fully implemented?

clintonb commented 4 years ago

This is fixed in release 2.0.4.

igor-krupa commented 4 years ago

I don't think is fixed. I forked @Martaver sandbox and changed to version 2.0.4

https://codesandbox.io/s/formik-example-jwwu5

I console.log isValid and initial is always true, then after first rerender it becomes false

mwmcode commented 4 years ago

@igorkrup yeah... I ended up using hannupekka's useEffect workaround.

djensen47 commented 4 years ago

I usually don't like to bump an issue but this is still happening despite being "stale." And I guess commenting will remove the stale tag.

djensen47 commented 4 years ago

@hannupekka @mustafawm How did you get the validateForm outside of the context of the <Formik> component? Or are you using useFormik?

tar-aldev commented 4 years ago

Any update on this? It seems like I can use dirty as a workaround, but still the issue exists. Initially isValid === true and isValidating === false. Sometimes on new page it is not visible, but when navigating between tabs it can be seen

crhayes commented 4 years ago

When specifying validateOnMount={true}, isValidating is false on first render. It seems like isValidating should be true in this case.

hmac2222 commented 4 years ago

+1

johnrom commented 4 years ago

I wouldn't be against setting isValidating to true on initial render when validateOnMount is true. It's technically a lie, so we'd have to make sure that if validation never runs (is that possible?) it would revert back to false at some point.

gael33 commented 4 years ago

+1 I suggest isValid should be false until at least one validation has been executed.

TomBerriot commented 4 years ago

+1

crhayes commented 4 years ago

I suggest isValid should be false until at least one validation has been executed.

An issue I have with this approach is that it's perfectly reasonable to pre-fill a form with valid data and specify validateOnMount={false}. In that case I would expect isValid to be true.

Forgive me if you meant that it should only behave this way when validateOnMount={true}. The intent was not clear from your comment.

parzivalClaus commented 4 years ago

When specifying validateOnMount={true}, isValidating is false on first render. It seems like isValidating should be true in this case.

Specifying validadeOnMount={true} works for what I need, thanks.

ghost commented 4 years ago

validadeOnMount: true not working with useFormik. Any updates on this?

charleshimmer commented 4 years ago

Same issue here. A hacky workout around for me right now is isInitialValid={false} which isn't always true. Would be better if validateOnMount={true} worked.

TNAJanssen commented 4 years ago

I also have the same issue, does anybody have a clue where the problems lies?

martaver commented 4 years ago

I think the difficulty with providing a comprehensive solution to this problem is that 'validation' can include custom validators that are async and can make requests to check validity. If we are re-evaluating validation state immediately on init, then potentially it would run all of those requests again which is undesirable.

I think the only viable solution to a problem like this is to capture 'validation state' and load it simultaneously with initial values, and this is non-trivial.

I'm not certain, but that's probably the reason there hasn't been a 'quick fix' for this. @jaredpalmer is this the case?

jaredpalmer commented 4 years ago

Well summarized. This is the problem. GitHub notifications@github.com wrote: “I think the difficulty with providing a comprehensive solution to this problem is that 'validation' can include custom validators that are async and can make requests to check validity. If we are re-evaluating validation state immediately on init, then potentially it would run all of those requests again which is undesirable.

I think the only viable solution to a problem like this is to capture 'validation state' and load it simultaneously with initial values, and this is non-trivial.

I'm not certain, but that's probably the reason there hasn't been a 'quick fix' for this. @jaredpalmer is this the case?”

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

martaver commented 4 years ago

So... its non-trivial... but can we do it anyway? :D

mrlaseptima commented 4 years ago

i have same issue . but i solve this with some codes : i set isInitialValid to a state that have a same name (isInitialValid). and change this state just with schema.validate every time my defaultValues changed.

const [isInitialValid, setIsInitialValid] = useState(false);'

 useEffect(() => {
    schema
      .validate(defaultValues)
      .then((res) => setIsInitialValid(true))
      .catch((err) => setIsInitialValid(false));
  }, [defaultValues]);

  return ( 
<Formik
        initialValues={defaultValues}
        enableReinitialize
        isInitialValid={isInitialValid} 
        validationSchema={schema}
       >
//my fields 
</Formik>
C-E-Rios commented 4 years ago

Formik@2.1.4

https://codesandbox.io/s/formik-example-forked-dgf5j?file=/index.js

akhatriST commented 4 years ago

Looks like the fix is not available in Formik@2.1.5.

Tried out 2.1.4 and it is working fine as expected with validateOnMount={true}

mariusLionte123 commented 3 years ago

I am usint 2.1.5 and validateOnMount={true} is not working.

Jared-Dahlke commented 3 years ago

as a new Formik user I just had to downgrade from 2.1.5 to 2.1.4 for validateOnMount={true} to actually work/fill the error object. Why was it removed in 2.1.5?

amazing tool by the way thank you for all of your work making this.

dommangetnicolas commented 3 years ago

I've done it by doing that:

const formRef = useRef<any>();

useEffect(() => {
    formRef?.current?.validateForm();
}, []);

<Formik
    innerRef={formRef}
    validationSchema={validation}
    initialValues={{password: ''}}
    validateOnMount
    onSubmit={(values, formikHelpers) => onSubmit(values, formikHelpers)}>
</Formik>
matin-deriv commented 3 years ago

When specifying isValid, you can get dirty as well and add it to your condition is_disabled={!dirty || isSubmitting || !isValid}

farzanfx commented 3 years ago

best approach to set initial validation to false I found a prop called : isInitialValid. here is my code and my initial validation to set submit button disabled or not I used it in such a way? const formik = useFormik({ initialValues: { firstName: firstName, lastName: lastName }, validationSchema: Yup.object({ firstName: Yup.string() .max(15, 'Must be 15 characters or less') .required('Required'), lastName: Yup.string() .max(20, 'Must be 20 characters or less') .required('Required'), }), isInitialValid : false });

and my button : <Button disabled={!formik.isValid} onClick={() => nextHandler()} >

superamxpruebas commented 3 years ago

best approach to set initial validation to false I found a prop called : isInitialValid. here is my code and my initial validation to set submit button disabled or not I used it in such a way? const formik = useFormik({ initialValues: { firstName: firstName, lastName: lastName }, validationSchema: Yup.object({ firstName: Yup.string() .max(15, 'Must be 15 characters or less') .required('Required'), lastName: Yup.string() .max(20, 'Must be 20 characters or less') .required('Required'), }), isInitialValid : false });

and my button : <Button disabled={!formik.isValid} onClick={() => nextHandler()} >

yes, this pretty much solves it (formik 2.2.5)

ghost commented 3 years ago

For now that works for me too, but there's this warning: Warning: isInitialValid has been deprecated and will be removed in future versions of Formik. Please use initialErrors or validateOnMount instead.

johnrom commented 3 years ago

isInitialValid is now !empty(initialErrors) (though you can continue using isInitialValid for now), so you should be able to do initialErrors={{ manuallyIndicatingAn: "Error" }}, or just run your validate() function on initialValues, which we don't do within a Formik render because we cannot prove it to be render safe / it may be expensive or async for some users


const validationSchema = Yup.object({ 
  firstName: Yup.string() .max(15, 'Must be 15 characters or less') .required('Required'), 
  lastName: Yup.string() .max(20, 'Must be 20 characters or less') .required('Required'), 
});

const initialValues = {
  firstName: '',
  lastName: '',
};

const MyForm = () => (
  <Formik 
    initialValues={initialValues} 
    validationSchema={validationSchema} 
    isInitialValid={validationSchema.validateSync(initialValues) /* or whatever Yup's API looks like, also memoize this */}
  />
);
flormasuyama commented 3 years ago

This is the workaround I found since isValid is true when the form loads: isEmpty belongs to lodash lib.

disabled={!dirty || !isValid || !isEmpty(errors)}

laurensdacosta commented 3 years ago

This is how I solved it: const InitialValidate = () => { const { values, submitForm, validateForm, dirty } = useFormikContext(); React.useEffect(() => dirty && (() => validateForm())(), [values, submitForm]); return null; };

Component: <InitialValidate />

Ideal because can be optionally used in a Stepped form.

schester44 commented 3 years ago

yikes

shinchannn commented 2 years ago

Seems it's been resolved in Formik 2.2.9

kodaicoder commented 1 year ago

for me it still not resolved in 2.2.9 I using this work around

<button  type="submit"  disabled={!formik.dirty || !formik.isValid | !Object.keys(formik.errors).length === 0 }>
            Confirm
</button>

it just a bit too long but it working good so far 😄

bruno-rilla commented 1 year ago

Still experiencing the same: I could solve it with the solution given by @flormasuyama disabled={!dirty || !isValid || !isEmpty(errors)}

Thank you @flormasuyama

SSOURABH58 commented 1 year ago

formik has a prop called validateOnMount, setting it to true solves the issue easily validateOnMount={true}

I think it's the intended way to do it, and with that, we can mark the issue closed 🟣

example :

<Formik
        validationSchema={ValidationSchema}
        initialValues={{ email: '', password: '' }}
        onSubmit={values => onSubmit(values.email, values.password)}
        validateOnMount={true}  // <----- add this and you are good to go : )
      >

any responses are appreciated 😉

AnandBhandari1 commented 1 year ago

In the ValidationSchema I did like this

 validateOnMount: true,
    enableReinitialize: true,

This worked for "formik": "^2.2.9",

kristof-mattei commented 1 year ago

Using useFormik() and doing validateOnMount causes my form to load with an enabled button and then within a split second disable.

rwb196884 commented 1 year ago

Is there a way to get validation to run initially? I can't get any of this to work -- seems to be completely fucked.

mizrabsheikh commented 1 year ago

Facing same issue on "formik?: "^2.4.2"

mbeltramin93 commented 1 year ago

Facing same issue on "formik?: "^2.4.2"

I don't, it seems to work with 2.4.3 version.

kristof-mattei commented 1 year ago

Managed to create a reproducible example:

https://codesandbox.io/s/formik-valid-when-it-shouldnt-be-h3hzxf

Best way to see it is deployed: https://h3hzxf.csb.app/ Refresh the page and you can see the submit button being enabled before it being disabled.

I've also added a 'log' of states that occur. In my opinion it should never be valid, as we load the form with invalid data.

Animation2

gabrielareia commented 10 months ago

Facing the same issue. We should have an immediate feedback when validateOnMount is true.