If there is an "external" component controlling initialValues passed to Formik, Formik never updates it.
This may be desired behavior, however in my specific case I need to re-render the form (and ideally, warn the user about unsaved changes) with new initialValues. If this is not a bug, any ideas on how to achieve this?
initialValues.When initialValues change, Formik should trigger a re-render and provide new initialValues in its render method.
CodeSandbox Link:
https://codesandbox.io/s/kworjlq7n3
EDIT: CodeSandbox demonstrates a working solution
Aaaaaaaand... solved couple minutes after submitting this.
The missing piece was enableReinitialize prop.
Since this is open now, I'll use the opportunity to ask if there is a way to
Thanks
Hummmm, OK, I see it is closed. Does that mean there is no way to check if the reinitialization happened?
@jaredpalmer ^
May I suggest to the authors of Formik that the default value of this defaults to true? It is counter-intuitive for it to behave this way without explicitly stating it
I just lost hours of coding because of this.
<Formik
enableReinitialize // missing piece!!
initialValues={props.initialValues}
working here
Doesn't work for me ...
does not work for me...
Worked for me.
Worked for me also, and I agree with @danieltodonnell , this is counter intuitive and I also spent hours searching the docs and the web to reach this thread as others have mentioned.
@linuxgg For god sake, you save my day
For me the presence of the enableReinitialize property doesn't change anything. If the form hasn't been touched (dirty = false) the form is re-rendered if any property in initialValues is changed from outside of Formik. So far as expected.
Once the form is changed by any user input (dirty = true), changes to the initialValues triggers the render function but does not re-write the values within the form.
Thanks for any suggestions!
enableReinitialize doesn't work for me either.
@killdash9 make sure you check the version. It looks like enableReinitialize was lost and then found 馃槃 in https://github.com/jaredpalmer/formik/pull/1399 - it's in v2.0.1-alpha.1 and presumably in later versions as well.
When I use enableReinitialize along with reac-text-mask, the value is not changed. Has anyone had this problem before
@iamvanja Thanks so much for posting this issue and its solution. Seriously was driving myself nuts.
enableReinitialize still doesn't work for me. The state changes but, inner values does not changes.
My code:
<Formik
initialValues={this.state.initialFormData} // this changes over time but still values don't get changed
validationSchema={validationSchema}
onSubmit={this.handleSellerPwd}
render={this.renderSellerPwdForm}
enableReinitialize={true}
/>
@meetzaveri wrong version
@jaredpalmer it would be great if the documentation provided a rationale for the choice to set enableReinitialize to false. I'm having trouble pinpointing the use case for it being false. Is it because formik can't tell how the properties are mapped to initial values? So an unrelated property could change, but the form's values shouldn't be reset because of it? But if that's the case, isn't this a design flaw in the protocol used between the parent component and the component that's withFormik-wrapped? In other words, it's something you'd want for the developer to fail during testing (if they change a property to a form that's unrelated to its values?). I still feel I don't quite get formik.
@jaredpalmer took me hours to track this one down too. I'm new to both React and Formik, so there might well be a really good reason why this is the default behaviour. But part of the promise of Formik is that it's "just React, and your Formik problems are really React problems", and it seems to me that having your form not update with your state/props is a case where Formik really goes against the grain of React (and, arguably, the whole point of using React).
e.g. My use case was that when my page loads, I get the user's existing data for them to edit from an API call in componentDidMount, which overwrote empty values set in the constructor. This is how tutorials tell you to do this sort of thing, so I'm sure that this pattern is pretty common? For details on how confused I got by this, see https://stackoverflow.com/questions/56246765/formik-values-not-updating-with-state/56252261#56252261
@robwold what version are you using?
@robwold initially this is exactly how Formik worked. However, a lot of folks passing in data from redux wanted a way to turn this off. So we came up with enableReinitialize.
@jaredpalmer That was a fast response, thanks! I'm on 1.5.1. I appreciate that for Open Source you've got to balance the competing interests of different users, all of whom owe you something and not the other way round - just wanted to provide some feedback, especially as I'm not the only one who's been caught out by this.
In v2, we are deprecating this behavior and giving you more control over resets
I get the impression that enableReinitialize depends on some lifecycle happening.
Because at first, like many commenting above, I had a hard time getting my form to show any data at all. Then I added enableReinitialize and since then it updates, when initially loading the data from graphql.
But I also have a situation (setting filters), when the form should update due to Mobx state changes.
The form dutifully re-renders every time the filter changes. But formik does not reinitialize its state.
How can I solve this?
I am now giving the Formik component a key of JSON.stringify(row) to force formik to update when my filter and thus my row object changes. But that is a bit of a hack.
@robwold initially this is exactly how Formik worked. However, a lot of folks passing in data from redux wanted a way to turn this off. So we came up with
enableReinitialize.
Isn't using redux a use case of having this on by default? I would think that update actions triggered in onSubmit should trigger a state change that will then be reflected in the form as well (via a new initialValues prop). If enableReinitialize is off, this intrinsic update behavior is lost.
I'm just interested in what aspect I am missing here.
Great work with formik btw @jaredpalmer !
Well add me to the list of folks who have had hours eaten up by this issue. Couldn't figure out why supplying new initial state once the Redux Saga API call had completed was not updating the DOM. Agree with everyone that this behavior should be on by default.
This thread saved me, thank you. Also agree that enableReinitialize should be enabled by default.
I am now giving the
Formikcomponent a key ofJSON.stringify(row)to force formik to update when my filter and thus myrowobject changes. But that is a bit of a hack.
after few hours of searching for answers and trying to fix this problem with re-render your "hack" is the only thing that worked! thanks!
Making enableReinitialize true by default would have been nice. Or, make it as a required prop if that is such a thing in react, so we are forced to put something in there, and will make appropriate decision accordingly. However, loving Formik, and appreciate your hard work.
Aaaaaaaand... solved couple minutes after submitting this.
The missing piece wasenableReinitializeprop.Since this is open now, I'll use the opportunity to ask if there is a way to
- listen on reinitialization
- cancel it
Thanks
Saved my life
Here small sandbox preview for those who need it or want to check the behavior of that (enableReinitialize):
https://codesandbox.io/s/pensive-platform-l1j47
I had problem with dirty form as @adammolnar mentioned so instead of reinitializing formik I have used form.resetForm(newValuesObject);
Docs:
https://jaredpalmer.com/formik/docs/api/formik#resetform-nextvalues-values-void
This flag fixed all of my issues, thanks y'all!
I am now giving the
Formikcomponent a key ofJSON.stringify(row)to force formik to update when my filter and thus myrowobject changes. But that is a bit of a hack.
This works with v1.5.1
@rob10e Yes, you are right. Works for me in v1.5.8
I'm dynamically generating part of the form, and it always gives me the error ...
Warning: A component is changing an uncontrolled input of type text to be controlled.
Setting enableReinitialize didn't help.
Here's a StackOverflow post with the issue ...
https://stackoverflow.com/questions/58205963/formik-update-initial-values-after-api-call
And here's a link for sandbox ...
https://codesandbox.io/s/test-dynamic-inputs-with-formik-xr9qg
Any help would be appreciated.
That error in my experience means you haven't set all field names. The initialValues property must point to an object with keys for each Field. If one is missing you get this error.
@godmar I just rendered all the inputs coming from the API, and still, that error occurs. Any idea what else could be wrong here?
I don't think you do. Output the values when the <Formik> renders. For instance, if your initialValues don't have a key user but later there's a Field with name="user" you get this.
@godmar I figured it out ...
You have to set value={field.value || ''} in the input inside the TextInput component or whatever type you're using in order to fix this issue.
adding " enableReinitialize prop " worked for me !! saved my day !! Thanks a lot !!
@godmar I figured it out ...
You have to setvalue={field.value || ''}in the input inside the TextInput component or whatever type you're using in order to fix this issue.
That has the same effect as making sure that all values are set in initialValues. The error occurs if a value is undefined, which you avoid with your || '' fallback.
@jaredpalmer: Frist of all, Formik is absolutely great! Thanks so much!
It does not really matter if the enableReinitialize setting is true/false but what is absolutely important that,
1.) enableReinitialize is mentioned in every example with a short note, including the Getting Started Guide.
2.) enableReinitialize has a better documentation. The Description is not obvious for a newbie. For a newbei sth. like "Tracking State" is much more obvious term. Resetting was the last I thought when I was looking for a solution.
So many people report here having searched hours to solve the problem (including me). Please make this more ovious. Thanks so much.
I am now giving the
Formikcomponent a key ofJSON.stringify(row)to force formik to update when my filter and thus myrowobject changes. But that is a bit of a hack.
Thank-you so much.I had tried : enableReinitialize and bumping up the version, nothing worked except this last resort.Just gave a key with unique value to formik.Probably helps someone in need.Had spent a lot of time worrying I would have to give up on formik because of this.
Going to tackle better in docs.
I am making a form with drop-down field that pulls in a list of data when the first time form renders. I am seeing similar behavior where the form-field does not update. In the example below, the "salutation" value can be seen as "Mrs" in "DisplayFormikState", but the salutation form-field initially shows the value "Mr". If I click in name field and then go out (triggering a refresh), then salutation picks up the correct value. Any suggestions?
// salutationArr wil contain salutations
const salutationArr = [];
// testVarRetrieveFunc is made to simulate network call
const testVarRetrieveFunc = () => {
return new Promise(resolve => setTimeout(() => {
salutationArr.push('Mr'); salutationArr.push('Mrs'); salutationArr.push('Dr');
resolve('setTimeout - done');
}, 500));
};
// just a wrapper to put extra logic if needed
const testVarRetrieveFuncWrapper = async (f) => {
console.log('started');
const result = await testVarRetrieveFunc(); console.log(result);
console.log('done');
f();
}
// React element for list of salutations
const SalutationListComponent = () => {
const [txt, setTxt] = useState('initTxt');
useEffect(() => {
if(txt==='initTxt') {
testVarRetrieveFuncWrapper(() => setTxt('doneTxt'));
}
}, [txt]);
return salutationArr.length===0 ? null : salutationArr.map(
a => <option key={a}>{a}</option>
);
};
const errorMessageDivMaker = (errorEltId) => {
return (errorMsg) => {
return <div className="input-feedback" id={errorEltId}>{errorMsg}</div>;
}
}
const App = () => (
<div className="app">
<Formik
initialValues={{ name: "1234", salutation: "Mrs" }}
initialErrors={{name: "testName-errorMsg"}}
initialTouched={{name:true}}
onSubmit={async values => {
await new Promise(resolve => setTimeout(resolve, 500));
alert(JSON.stringify(values, null, 2));
}}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleReset
} = props;
return (
<Form>
<label htmlFor="salutation" style={{ display: "block" }}>
Prefix
</label>
<Field as="select" name="salutation" value={values.salutation}>
<SalutationListComponent />
</Field>
<label htmlFor="name" style={{ display: "block" }}>
Name
</label>
<Field name="name" className={
errors.name && touched.name
? "text-input error"
: "text-input"
}/>
<ErrorMessage name="name" render={errorMessageDivMaker("name")}/>
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</Form>
);
}}
</Formik>
<MoreResources />
</div>
);
render(<App />, document.getElementById("root"));
Or you can set the element key like so:
<Formik key={someId} ...
This tells react that this element has changed and should be re-rendered.
https://en.reactjs.org/docs/lists-and-keys.html#keys
Keys help React identify which items have changed, are added, or are removed. Keys should be given [...] to give the elements a stable identity
you guys just need to follow the steps bellow:
btw initialisation its skipped if values are deeply equal, i don't understand why is this possible but my point of view is: there should be easy shallow equal comparation and the parent component should be responsible for proper value. And also no need to have initialisation property (and if u are not able to implement it, think about yourself)
// ugly hack
const resetRef = useRef();
useEffect(() => {
resetRef.current && resetRef.current();
}, [initialValues]);
const render = ({ resetForm }: any) => {
resetRef.current = resetForm;
return ( .... )
};
<Formik render={render} ...
you guys just need to follow the steps bellow:
- Set the enableReinitialize on Formik component;
- Set an initial state for the initialValues variable (useState for who is working with hooks);
- Update the initialValues to the new one inside your side effect (useEffect for who is working with hooks or componentDidMount / componentDidUpdate).
Im not sure this is working. This is and example with select from external lib
https://codesandbox.io/s/material-ui-formik-components-ychoc?file=/src/index.js
Notice "gender" will not refresh the select
@multivoltage you have to pass enableReinitialize={true} to Formik.
@johnrom Oh my god. I put attribute like html :) Sorry.
you guys just need to follow the steps bellow:
- Set the enableReinitialize on Formik component;
- Set an initial state for the initialValues variable (useState for who is working with hooks);
- Update the initialValues to the new one inside your side effect (useEffect for who is working with hooks or componentDidMount / componentDidUpdate).
This worked perfectly for me. Thank you
After reinitialise validations are not working...!!!
key={
Worked for me. Best answer!
It works for me by setting initialValues to empty string with useState
const [initialValues, setInitialValues] = useState({
name: "",
age: "",
email: "",
});
Then set enableReinitialize to true for Formik.
enableReinitialize={true}
Then use UseEffect to update the form when the screen loads.
I encountered one error where enableReinitialize doesn't do anything. The problem is that I wrapped around Formik to create customized Form component and I forgot to pass enableReinitialize to my customized form component... That took me an hour to figure out... Hope my experience could help if enableReinitialize doesn't work for you.
Adding enableReinitialize saved my bacon! => Thank you!
@Andy-ZYA Thanks. It worked for me.
When I use enableReinitialize along with reac-text-mask, the value is not changed. Has anyone had this problem before
I'm facing this same issue when using
When I use enableReinitialize along with reac-text-mask, the value is not changed. Has anyone had this problem before
I'm facing this same issue when using component from react-input-mask. I'm looking for the solution for this scenario as well.
That's my case too 馃槩 , do you managed to figure out how to resolve this?
when i use enableReinitialize in my form, every time when i change autocomplete component and call api then setState. my componen re-renders and my form back to initialValues again. i want reinitialize only if form is not dirty. how can i achieve this ?
@rom5jp @saymow @DevMammadov codesandbox repro will help someone investigate your specific issue
@rom5jp @saymow I'd guess you need to translate the handleChange or handleBlur from your third party input library of choice to Formik's api
@devmammadov I'd guess you need to memoize your API call / initialValues so that repeated API calls don't cause the reinitialization
const MyForm = () => {
const [apiResult, updateApiResult] = useState();
const [initialValues, updateInitialValues] = useState(getDefaultInitialValuesWithoutApiResult());
const apiUpdater = useCallback(async () => {
const result = await fetch('/api/endpoint');
result && updateStateFromApiResult(result);
}
useEffect(
() => updateInitialValues(getInitialValuesWithApiResult(stateFromApiResult)),
[Boolean(apiResult)] // only update once when receiving first result
);
return <Formik initialValues={initialValues} />;
}
But there's not much I can help with without a repro.
<Formik enableReinitialize // missing piece!! initialValues={props.initialValues}working here
I have the same issue. Thanks so much for solution
Or you can set the element
keylike so:
<Formik key={someId} ...This tells react that this element has changed and should be re-rendered.
https://en.reactjs.org/docs/lists-and-keys.html#keys
Keys help React identify which items have changed, are added, or are removed. Keys should be given [...] to give the elements a stable identity
Using key is a better approach. I'm dynamically updating the Form's initial values and enableReinitialize breaks the form state (e.g. touched fields).
<Formik enableReinitialize // missing piece!! initialValues={props.initialValues}working here
I have the same issue. Thanks so much for solution
worked for me thanks
Worked for me. Thanks!
@rom5jp @saymow @DevMammadov codesandbox repro will help someone investigate your specific issue
@rom5jp @saymow I'd guess you need to translate the handleChange or handleBlur from your third party input library of choice to Formik's api
@DevMammadov I'd guess you need to memoize your API call / initialValues so that repeated API calls don't cause the reinitialization
const MyForm = () => { const [apiResult, updateApiResult] = useState(); const [initialValues, updateInitialValues] = useState(getDefaultInitialValuesWithoutApiResult()); const apiUpdater = useCallback(async () => { const result = await fetch('/api/endpoint'); result && updateStateFromApiResult(result); } useEffect( () => updateInitialValues(getInitialValuesWithApiResult(stateFromApiResult)), [Boolean(apiResult)] // only update once when receiving first result ); return <Formik initialValues={initialValues} />; }But there's not much I can help with without a repro.
I used the enableReinitialize flag to the opening tag of Formik, the behavior I noticed when declaring initialValues as a variable then changing it's value after loading the form is that the actual change happens to values inside the form itself and the initialValues stay the same, which does not call for a reload of the form.
the solution as @johnrom is to use the useState hook to avoid this issue.
you guys just need to follow the steps bellow:
- Set the enableReinitialize on Formik component;
- Set an initial state for the initialValues variable (useState for who is working with hooks);
- Update the initialValues to the new one inside your side effect (useEffect for who is working with hooks or componentDidMount / componentDidUpdate).
This saved my life. I tried for weeks to figure out a pattern but couldn't figure it out until reading this comment. I do want to point out that if you are using this pattern remember to still use Formik as your source of truth, just refresh your initials using redux or whatever state (don't move your form state or form actions into redux).
Also I want to point out a cleaner alternative to this pattern using withFormik and mapPropsToValues instead of useState:
const FormikForm = withFormik({
mapPropsToValues: (props) => {. //***This function runs whenever your PROPS change and it updates the initial values
let profileName = 'Your Initial Profile Name'
let competitors = [{id: 1, name: 'Initial Competitor Name'}]
if (props.basicInfo.profileName.length > 0) { //If you loaded a new profile name from an ajax call or redux or something , then update your initial values here!
profileName = props.basicInfo.profileName
}
if (props.competitors.length > 0) { //ditto
competitors = props.competitors
}
return {
basicInfoProfileName: profileName,
topCompetitors: competitors,
}
},
enableReinitialize: true,
validateOnMount: true,
validationSchema: schemaValidation
})(MyMainComponent)
I know this is an old thread, but I think enableReinitialize being false by default is a more sane choice. mostly forms are ephemeral, and you want the values to be set once, worked with, then sent out to a handler and be done.
Aaaaaaaand... solved couple minutes after submitting this.
The missing piece wasenableReinitializeprop.Since this is open now, I'll use the opportunity to ask if there is a way to
- listen on reinitialization
- cancel it
Thanks
<Formik enableReinitialize // missing piece!! initialValues={props.initialValues}working here
I have the same issue. Thanks so much for solution
render(){
let formikInitials = {
//setting up initial values.
};
return(
<>
initialValues={formikInitials}
.... >
.....
>
);
Thanks for the suggestion! facing this issue for the last 3 days didn't find any solution till found yours! Thanks again!
@iamvanja a simple solution that took me so much time to find
Most helpful comment
Aaaaaaaand... solved couple minutes after submitting this.
The missing piece was
enableReinitializeprop.Since this is open now, I'll use the opportunity to ask if there is a way to
Thanks