Formik: Initial validation or initialErrors prop

Created on 3 Dec 2017  路  42Comments  路  Source: formium/formik

What if the user has set some field values, and we do store them somewhere, and when the user returns to the app those values are invalid. Is there no way to run validate on initialization?

Medium Enhancement

Most helpful comment

+1 for this use case.

I would like to display validation errors & disable form submission for a record that is already persisted, and will now be _newly_ invalid based on some recent changes in the underlying business logic.

So I would like the initial form load to reflect this, but ran into the same issue where the validator function is not run until some field is touched. This has the effect of leaving the form's submit button initially _enabled_ for what we consider to be an invalid record.

For now I am working around this by moving logic out of the validator function and into various form components, which seems a shame given all the wins we've enjoyed by relying on Formik's awesome default behavior so far.

All 42 comments

@jaredpalmer whaddya think?

The tricky thing is with keeping formik compatible with SSR. You can set the initial validity using isInitialValid

Can you explain that a little? If the form is invalid and you can SSR that, wouldn't you also want to SSR with the error validation (help messages) on whichever fields have issues?

Formik can't run validations (esp. async ones) before mount because calling setState in componentWillMount is really bad and also inconsistent

What about the errors equivalent of isInitialValid? Like initialErrors?

I guess I'm trying to ask you if you agree there is a use case. Because if there is, I'm sure there's a way to do it.

+1 for this use case.

I would like to display validation errors & disable form submission for a record that is already persisted, and will now be _newly_ invalid based on some recent changes in the underlying business logic.

So I would like the initial form load to reflect this, but ran into the same issue where the validator function is not run until some field is touched. This has the effect of leaving the form's submit button initially _enabled_ for what we consider to be an invalid record.

For now I am working around this by moving logic out of the validator function and into various form components, which seems a shame given all the wins we've enjoyed by relying on Formik's awesome default behavior so far.

I've also encountered a use-case for this, where a form is persisted, even in an invalid state, since it is shared between multiple users and takes time to complete.

I would like the form to be validated immediately on initialization, so that its isValid and errors are correctly populated at all times.

I really don't see why the form's 'validation state' shouldn't reflect its values at all times.

If it's not appropriate to show validation errors for whatever reason, then I think of this a display problem, not a validation problem.

When you do only onBlur validation, having initialErrors would help also.

I ran into this problem when you have an initially invalid form and you only validate onBlur, after the form becomes dirty (eg. typing in a field) the isValid key changes to true because it only checks the form's errors which is empty at that point. Even though the initial form was invalid and it doesn't run any validation yet.

Hi all, any example of this implementation? I couldnt find it working without having to do a change in the form. Im looking for a solution to get isValid of the form on load. Im using Yup validations and wrapping it WithFormik. Thanks

@anandaroop The workaround that I am using is to call this.refs.formikForm.getFormikBag().validateForm() in the parent's componentDidMount method.

Hey all,

not sure of the state of this, but we also do have a usecase for initialErrors field. Our app uses redux-saga (ugh), meaning the async logic is detached from the form submission. We therefore have no way to call setErrors. Instead, redux will redraw our component and provides validation errors as props if any. There is no straightforward way to pass these errors on to the form at the moment. Just to be clear, I can imagine a lot of hacky ways to do it, but I'd rather not.

AFAIK the current validateOnLoad PR https://github.com/jaredpalmer/formik/pull/622 would not help us as the validation logic is performed in another layer of our app.

If the maintainers agree, I would be willing to implement the initialErrors feature. At first glance, it has nice symmetry with initialValues too, so it shouldn't hurt the API too much.

I think initialErrors makes a lot of sense. Feel free to submit a PR. It should be almost identical to initialValues.

Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.

Just echoing that this is an important use case for us as well. we SSR forms and would like to populate the formik form with server errors without resorting to custom API at this point

The solution to initial validation on load is quite simple:

in your form component, the one which is wrapped into withFormik or Formik (I've tested with the latter) add:

  componentDidMount() {
    const { isValid, validateForm } = this.props;

    if (!isValid) {
      validateForm();
    }
  }

@iamdanthedev I realize this, but there are some errors that cannot be figured out on the client without API calls (i.e. database constraints, etc)

622 Looks like pretty simple change to get this to work. Any update on when this might get merged in? Seems that a lot of people - myself included - could really use this functionality.

K I'm on it

Alright PR is fixed/updated: #622 waiting on the man.

Thank you! 馃憤

@ianks So are you not using an API to validate fields on the client after page load (as in after SSR)? You make it sound like you're trying to avoid doing so.

To me, SSR/initialErrors seems like an unnecessary optimization... but only because I haven't done it perhaps. Have you figured out already how you would do it? Would you store errors in redux (which makes me cringe) in order to get them to nested components? Part of the appeal of formik is keeping form states out of redux.

Reviewing now. This is a large change, but understandable.

any news on this? I've tried using the 'ref' solution above, but it doesn't seem to be working for me.

Is there some estimate on when this might get merged?

There are possible cases where some form fields are no longer valid and you should prevent saving (even though they were at the time of first submit). We need to resort to some hacks to get around this, and having this PR merged would solve all the problems.

I tried this and this seems to work fine:

class FormSubmitter extends Component<{ submitForm: () => void}> {
  componentDidMount() {
    this.props.submitForm();
  }
  render() {
    return null;
  }
}

and use it like this:

const MyForm = ({
  submitForm,
}: FormikProps<FormValues>) => (
  <Form>
    <FormSubmitter submitForm={submitForm}/>
    ...
  • edit: componentWillMount to componentDidMount

@0xR to be safe with React master branch move that to componentDidMount

This or an equivalent will be in v2 btw.

Interesting, I tried doing it inside the render loop and I got a suggestion for componentWillMount.

@jaredpalmer Does that mean that #622 won't be merged in prior to a v2 release? Do you have a recommended temp fix in the meantime? There are several options people have posted here but they haven't seemed to work for me so far... (there is a decent chance I'm doing something wrong though as well)

@jpserrett if you have the same values in the state as the initialValues prop, the isValid prop will be the isInitialValid prop, because dirty is false in that case: https://github.com/jaredpalmer/formik/blob/master/src/Formik.tsx#L563

So, it's not enough calling the validateForm prop to know if the form is initial valid or not, you should call the validateForm and check the errors object instead of the isValid prop

state = {
  wasInitialValidated: false
}

async componentDidMount() {
  await this.props.validateForm()

  this.setState({
    wasInitialValidated: true
  })
}

render() {
  const isValid = Object.keys(this.props.errors).length === 0

  ...
}

How would you fire initial validation when using <Formik> component which uses render props pattern?
EDIT: I'm doing it inside render function and it works

@jaredpalmer is there a chance Formik will get this functionality in v1, or should we wait for v2?

Also, looked at the PR provided in this thread, unfortunately what it does is calling validate handler, thus not possible to "inject" errors from top. This can be achieved with refs.

If there is change of shipping with v1, I may create PR for this one.

For anyone coming from Google Search, until this functionality will be built in, use this:

class ParentComponent extends Component {
    constructor(props) {
         super(props);
         this.form = React.createRef();
    }

    componentDidMount() {
        // runs "on load" and validates the form immediately with the initialValues
        this.form.current.validateForm();
    }

    render() {
        <Formik
            // other props...
            ref={this.form}
        >
            // children...
        </Formik>
    }
}

This is for pretty basic usage...if you actually need to check validity on init and also "trick" your app into believing that the form was filled in, focused and blurred, look at this comment.

If you just need to control the "disable" prop of a submit button, my solution works just fine.

If you're using React >= 16.8, you can do the above with useRef and useEffect:

const form = useRef();
useEffect(() => {
  if (form.current) {
    form.current.getFormikActions().validateForm();
  }
});
return <Formik ref={form} ...

If you're using React >= 16.8, you can do the above with useRef and useEffect:

const form = useRef();
useEffect(() => {
  if (form.current) {
    form.current.getFormikActions().validateForm();
  }
});
return <Formik ref={form} ...

React >= 16.8 useEffect and useRef worked for me ...

The solution to initial validation on load is quite simple:

in your form component, the one which is wrapped into withFormik or Formik (I've tested with the latter) add:

  componentDidMount() {
    const { isValid, validateForm } = this.props;

    if (!isValid) {
      validateForm();
    }
  }

What about the update form where the initial values are loaded from redux state using mapPropsToValues and enableReinitialize: true?? How will you initially validate the form in that case?

@mjangir with the HOC? It injects the validateForm as Prop

  useEffect(() => {
    if (form.current) {
      this.props.validateForm();
    }
  }, [form]);

You can also pass a function to isInitialValid:

isInitialValid={formik => {
    // use some custom function
    return validate(formik.initialValues);
}}

Unfortunately, this won't put any errors into your form.

Added initialErrors v2 #1626

calling validateForm on mount is not working for me at all. tried setFieldTouched as well.

validate is called when validateForm is called, and it's returning the errors object. do I just need to set the error manually as well?

 <Formik
      initialValues={{text: '', password: '', active: ''}}
      onSubmit={values => {
        console.log(values);
      }}
      validateOnMount={true}
      validationSchema={signInSchema}>

Use this for validating initial values

validateOnMount={true}

validateOnMount={true} still doesn't work for me. I have updated to version 2.1.5.

Use case:
I have some initial values for the form, but the form is not validated on mount.
So the isValid is false for this case and I have disabled the submit button, but since the errors are not shown for the initial values on the field unless onBlur() is triggered on each field the user doesn't know why the submit button is disabled.

A work around for this (for eg the phone number field is):
useEffect(() => {
values.phone && setFieldTouched("phone");
}, []);
But I don't want to do it for all the fields.
Does anyone know how to fix this?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaredpalmer picture jaredpalmer  路  3Comments

Jucesr picture Jucesr  路  3Comments

emartini picture emartini  路  3Comments

outaTiME picture outaTiME  路  3Comments

pmonty picture pmonty  路  3Comments