Formik: Set Field Value not working when called from didmount

Created on 21 Sep 2018  路  20Comments  路  Source: formium/formik

Current Behavior

When I call setFieldTouched and setFieldValue in the componentDidMount method, the value is not applied in the values object but in the touched object it is.

Steps to Reproduce

Expected behavior


1 Create a form with formik, it can be with the Form component or the withFormik hoc.
2 Call setFieldValue in the componentDidMount and check the values prop for any change.

Suggested solution(s)


Seems like in some way the form is not ready to change values, since a workaround could be to use the setState callback or setTimer( () => this.props.setFieldValue(), 0 )

Additional context


This happens in 1.0.3 since in 1.0.2 seems to work fine

CodeSandbox Link:


  • Formik Version: 1.3.0
  • React Version: 16.5.1
  • TypeScript Version: na
  • Browser and Version: Chrome 69.0.3497.100
  • OS: MacOs High Sierra 10.13.6
  • Node Version: 10.8.9
  • Package Manager and Version: yarn 1.9.4
stale

Most helpful comment

:point_up: Stale? I've just encountered this issue and it's pretty annoying, any fix in sight? :confused:

All 20 comments

Can you make a codesandbox link?

Sure,

This is a working sample with 1.2.0:

https://codesandbox.io/s/kxnqxr2wn3

You can see that the sample value and the touched is set for the id sample after the componentDidMount

And here is a sample with 1.3.0:

https://codesandbox.io/s/1q39284r57

In this case only the touched prop is set after the componentDidMount

I am also experiencing it with the connect() method. Only after I handle it with setTimeout(someFunction, 0) it works normally. But I don't like it since it seems "hacky".

Same problem here with setFieldValue since 1.3.0 and the suggested workaround works for me as well, but I totally agree that it's hacky.

I looked for the cause for the problem and this PR seems to be it: #895
Is this really wanted behaviour to prevent changing the state before the whole form component would be mounted?
Thanks.

The same issue, it's not possible to populate dynamically form with some values inside componentDidMount method.

@jaredpalmer any update on this issue ?

More broadly, you can't call any of the FormikBag functions that modify the formik state, e.g. form.setSubmitting(). This is because the didMount flag in the <Formik> component doesn't get set to true until componentDidMount, but componentDidMount is called for child components before the parent.

I'm not sure what a good solution is here. Adding didMount to the formik state would allow child components to get notified of when the parent form has mounted, but this feels messy. Perhaps the FormikBag could include an onMount() method that takes a function as its sole argument. That function in turn would get called in the componentDidMount method of <Formik>

Same problem, can't set field value on componentDidMount. I need to sync some form fields with redux store, but I can't use mapStateToProps because it will reinitialize whole form... @jaredpalmer is possible to do something with this issue? Thanks

The same problem, I have nested Formik (multi-page in the same Formik).
my form is sync with redux on init,
but when I try to run setFieldValue in didMount of child component noting happen until I'm using setTimeout with 0

Same. Would be great if we could get a didMount callbback of sorts which passes a FormikBag back so we can do any handling with formik values without having to write messy code in the render method.

same problem with setFieldValue on a sub-sub component with a dynamic created value. solved sufficient by grading down to 1.0.2

We are ultimately taking the approach I suggested in my previous comment. We already had a Form class component that wrapped the main Formik component. We have now added the following methods/properties:

class Form extends React.Component {

  onMountCallbacks = [];

  componentDidMount() {
    if (this.onMountCallbacks.length) {
      for (const fn of this.onMountCallbacks) fn();
    }
  }

  registerOnMountCallback = fn => {
    this.onMountCallbacks.push(fn);
  };

We then pass down onMount: this.registerOnMountCallback as an additional property on the usual FormikBag in the form's render method.

@ericbiewener I can't seem to find that a solution.. right now using formik in a more complex way with nested dynamic fields is not possible.

and @AndreKelling I wouldn't recommend on downgrading to 1.0.2 because it is before the performance fixes.

For anyone encountering this bug, I've found a workaround, it's not pretty but it works.
If our problem is that formik won't initiate any updates until it is mounted, I used setTimeout with 1msec to make sure what I want happens inside componentDidMount and after formik IS mounted.

image

:point_up: Stale? I've just encountered this issue and it's pretty annoying, any fix in sight? :confused:

Had to run a validateForm() inside a componentDidUpdate, but kept validating using the previous values. Wrapping it in a setTimout did the trick.

  componentDidUpdate(prevProps) {
    const {fileId, validateForm} = this.props;
    if (prevProps.fileId !== fileId) {
      setTimeout(() => {
        validateForm();
      }, 0)
    }
  }

In React Native this worked for me:

componentDidMount() {
    InteractionManager.runAfterInteractions(() => {
      this.props.setFieldValue('test', 123);
    });
}

Got the same issue. This fixed it for me:

setTimeout(() => { props.setFieldValue('dateOfBirth', e) }, 1)

@jaredpalmer Hey, any updates? I'm facing this issue as well.

Forgive me if I'm not quite understanding the use-case of setting the value in the componentDidMount method, but the implementation of setting values in the mount phase seems a little bit like an anti-pattern.

Typically, if I'm initializing the form, I leverage two props provided by Formik: initialValues and enableReinitialize. I move the initialValues state up to the parent component so that I can pass it as props down to the form component. enableReinitialize will allow the form to reset the initialValues once they change in the parent component (either through an asynchronous network request or simply by setting the state). If you're not setting the form dynamically through an async request, I would strongly suggest simply just setting the initial values statically as opposed to using setState in the mount phase (this will avoid an unnecessary additional render).

Now, while this solves the values issue, it doesn't solve the touched issue. My only work-around for that is using componentDidUpdate to detect when the value populates in the form, then you can use the setFieldTouched

componentDidUpdate(prevProps) {
  if (prevProps.values.sample === '' && this.props.values.sample) {
    this.props.setFieldTouched('sample');
  }
}

I forked the first sandbox posted and altered it for this specific example. https://codesandbox.io/s/formik-example-8se6d

I'm not implying this isn't a bug--I'd have to look at the code a little bit deeper to understand what's happening, but I'm just suggesting a different approach that might avoid some other pitfalls.

I hope I understood the use-case enough and that this answer is both relevant and helpful!

How you would want to get this working in componentDidMount lifecycle method without using setTimeout is to create an async method which calls the setFieldValue.

componentDidMount() {
   initValues()
}

async function initValues() {
   return await setFieldValue("field", value)
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Jucesr picture Jucesr  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments

giulioambrogi picture giulioambrogi  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments

Jungwoo-An picture Jungwoo-An  路  3Comments