GitHub discussion about this issue - https://github.com/jaredpalmer/formik/issues/529
I passed formik values to a function that is called in <Field/>
's onChange but when I type in the <Field/>
, the last character does not fall into the function.
CodeSandbox - link
Screenshot:
Minimal Code Instance:
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, FieldArray } from "formik";
function App() {
//------ HERE ----------
const getValues = values => console.log(values.fields[0]);
//----------------------
return (
<>
<Formik
initialValues={{ fields: [""] }}
onSubmit={(values, actions) => {
actions.setSubmitting(false);
console.log(values);
return false;
}}
render={({ setFieldValue, values }) => (
<Form>
<FieldArray
name="fields"
render={() => (
<Field
type="text"
name="fields.0"
placeholder="Write something"
onChange={e => {
setFieldValue("fields.0", e.target.value);
//---------------
getValues(values);
//---------------
}}
/>
)}
/>
</Form>
)}
/>
</>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
it is very simple just do console. log(formik. values) and you will get all the values without submitting it.
You don't need to use value={values.name} you can use Formik props like onSubmit to get your values. Also what version are you using? please note that render method has been deprecated.
Initial field values of the form, Formik will make these values available to render methods component as values . Even if your form is empty by default, you must initialize all fields with initial values otherwise React will throw an error saying that you have changed an input from uncontrolled to controlled.
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
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.
componentDidUpdate
(or any other comparable React Lifecycle method) and listening for change in the props.this.props.values
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With