-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
setFieldValue creates infinite loop #2858
Comments
I've got something similar with import React from "react";
import { Formik, Form, Field } from "formik";
const Example = ({title, boxes}) => {
const handleReset = (values, {resetForm, setValues, ...formikBag}) => {
resetForm();
//resetForm({}); also causes infinite re-renders
};
const renderBoxes = (boxes = []) => boxes.map(({label}, i) => (<Field key={i} type="checkbox" name={label}/>));
return (<div style={{width: "100vw", height: "100vh"}}>
<Formik enableReinitialize={true} initialValues={{title, boxes}} onSubmit={() => console.log("submitted")} onReset={handleReset}>
{({values}) => (
<Form>
<div>
<div>
<h1>{title}</h1>
</div>
<div role="group">
{renderBoxes(values.boxes)}
</div>
<div>
<button type="reset">Reset</button>
</div>
</div>
</Form>)
}
</Formik>
</div>);
};
export default Example; Your environment
|
When you inline the function prop for onChanheValue you are recreating it on every render, it is then firing an effect when it changes causing the infinite loop. You need to wrap the callback with useCallback before passing it down. |
@jaredpalmer, thanks for getting back to us so quickly on this, much appreciated. In the case of import React, {useCallback} from "react";
import { Formik, Form, Field } from "formik";
const Example = ({title, boxes}) => {
const wrappedHandleReset = useCallback((values, {resetForm, setValues, ...formikBag}) => {
console.count("resetForm");
resetForm();
}, []);
const renderBoxes = (boxes = []) => boxes.map(({label}, i) => (<Field key={i} type="checkbox" name={label}/>));
return (<div style={{width: "100vw", height: "100vh"}}>
<Formik enableReinitialize={true} initialValues={{title, boxes}} onSubmit={async () => null} onReset={wrappedHandleReset}>
{({values, handleReset, resetForm}) => {
return (
<Form>
<div>
<div>
<h1>{values.title}</h1>
</div>
<div role="group">
{renderBoxes(values.boxes)}
</div>
<div>
{/*All three of these cause the same issue*/}
<button type="reset">Reset</button>
<button type="button" onClick={handleReset}>Reset (Explicitly Bound to onReset)</button>
<button type="button" onClick={(e) => {
e.preventDefault();
resetForm();
} }>Reset (imperative resetForm)</button>
</div>
</div>
</Form>)
}}
</Formik>
</div>);
};
export default Example; I've read the docs, am I missing something here? |
@maddhruv, any chance we can reopen this, or reopen the separate issue I had for resetForm? |
Thanks @jaredpalmer Just for reference if anyone ends up in this thread. Here is the fix function Demo() {
const initialValue = 100;
const {handleSubmit, setFieldValue} = useFormik({
initialValues: {
value: initialValue
},
onSubmit: (values) => {
console.log(JSON.stringify(values, null, 2));
}
});
const handleChangeValue = React.useCallback((value) => {
setFieldValue("value", value);
}, [setFieldValue]);
return (
<form onSubmit={handleSubmit}>
<CustomInput
initialValue={initialValue}
scale={10}
onChangeValue={handleChangeValue}
/>
</form>
);
} |
@horsemanV If I am not mistaken you provided a
which is called every time the form is reseted. In that call callback, you are reseting form again, which is causing the infinite loop |
Ah, because resetForm fires an event that then gets caught again by the handler. Great catch, thanks. |
Btw I actually do have a follow up question on my solution from before. In the solution above it requires "the user" to make sure to wrap the function FormikInput(props) {
const [field, meta, helpers] = useField(props.name);
// this causes infinite loop!
return <CustomInput {...props} onChangeValue={helpers.setValue} />
}
export default function Demo() {
const initialValue = 100;
return (
<Formik
initialValues={{
value: initialValue
}}
onSubmit={(values) => {
console.log(JSON.stringify(values, null, 2));
}}
>
<Form>
<FormikInput
initialValue={initialValue}
scale={10}
name="value"
/>
</Form>
</Formik>
);
} Another solution could be to fix it directly in React.useEffect(() => {
onChangeValue(value * scale);
}, [value, scale]); // removed onChangeValue |
@mr-bjerre we will eventually figure out how to return stable setters so that this workaround isn't necessary, but for now you can use https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback Using
|
There's a ton of related info here if you're in for a read: #2268 If this answers your question, we can close this as a duplicate of the above issue. |
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 60 days |
I have the same problem using the latest versions of Formik and React, has anyone looked into this issue? |
🐛 Bug report
Current Behavior
I have an input component that has some internal state (i.e. the inputs are made on another scale - e.g. values are written in millions instead of units. But the state of interest are always just the units.). This component only takes an initial value and not the current value as props. It also exposes a prop called
onChangeValue
which is basically a callback with the current value as input.Expected behavior
The following should update
formik.values.value
but instead I get an infinite loop.Reproducible example
Solution without formik
The following solution works without using formik
Your environment
The text was updated successfully, but these errors were encountered: