Issue with values Formik

If I understand your question correctly, you need a reliable way to get the latest state of Formik values, right after setting a Field's value & trigger another method/function/event based on this 'new' set of values. The obvious hindrance here is that setFieldValue() is an asynchronous method that internally uses setState() & hence there is no direct way to achieve this in a way you described / expected.

  • I wouldn't recommend the workarounds (setTimeout, random await just to bypass the nextTick, etc) mentioned in the github issue (https://github.com/jaredpalmer/formik/issues/529) since they are non-deterministic.
  • One way that worked for me is using componentDidUpdate (or any other comparable React Lifecycle method) and listening for change in the props.
  • Formik exposes the field values in React component's props at this.props.values
  • When you use componentDidUpdate, you just would need to compare the previous props to new props to check if something has changed.

Here is a minimal code to show what I mean.

const _ = require('lodash');

class Sample extends React.Component {
  componentDidUpdate(prevProps) {
    if(!_.isEqual(prevProps.values, this.props.values)) {
      // At least one of the Formik fields have changed. And this.props.values holds this newest data about of the fields.
      triggerWhateverWithNewValues(this.props.values);
    }
  }
}

Hope this helps.


This answer is based on Formik v1.5.7

You are trying to get stale value of values in your code right after calling setFieldValue which is internally doing async operation so you cant expect values being changed right after it (call) returns. Thus when you try to log value out of values you get state object during current (running) render cycle.

When the values do change, Formik re-renders the form in which it will have desired value that you are trying to get.

I moved up the code in your example to explain this.

Here's the updated code link.

Since this question is updated after my answer, I'll comment about this specific GitHub issue. The issue is in enhancement development request and solutions in the comments will not work in your example as values will still be "OLD" values.

Here's the reason why async await solution will not work in this case:

onChange={async e => {
    await setFieldValue("fields.0", e.target.value);
    getValues(values);
}}

Above code upon click event awaits on function called setFieldValue which gets executed and internally sets state which is async operation placed in the execution stack. Call returns and console.log logs values which happens to be existing render cycle values.

Hope that clarifies.


I am not familiar with formik and I just took a quick look on the implementation. But here's my two cents on the issue.

Why are the values not updating after each call?

You are trying to get the values before the rerendering is happening and the value you have is the old one always, because the component is not updated yet.

To verify try to add console.log(values.fields[0]); in the App component before the getValue definition.

Why is the call happening before the rerendering?

The onChange implementation will trigger the handleChange in which it's a memoized function call that will not be triggered if you have the same value BUT you changed the value.

handleChange will be triggered if the executeChange value is different and executeChange also memoized that depends on setFieldValue or the state.values.

setFieldValue is an event callback that only update the ref after each render. That's from formik docs // we copy a ref to the callback scoped to the current state/props on each render and that didn't happen yet in your case -useEventCallback-.

Once the state is updated by the action then your component will be updated but your function is not called yet.

setFieldValue will try to validate the input and return a promise and bubble that up to executeChange and handleChange and it treats that as a low priority so it's not a blocking call.

A quick solution might be is to use onKeyUp instead of onChange I think that will bypass the useCallback and actually updates the component with higher priority call

function App({ values, setFieldValue }) {
  console.log("Rerendering",values.fields[0])
  //------ HERE ----------
  const getValues = () => console.log(values.fields[0]);
  //----------------------

  return (
    <div className="formik-wrapper">
      <Form>
        <FieldArray
          name="fields"
          render={() => (
            <Field
              type="text"
              name="fields.0"
              placeholder="Write something"
              onKeyUp={e => {
                setFieldValue("fields.0", e.target.value);
                getValues();
              }}
            />
          )}
        />
      </Form>
    </div>
  );
}

I hope this can help or at least lead for some help.

Check formik implementation