Open antonvialibri1 opened 2 years ago
Is there a workaround for this?
I found a workaround to this subtle bug by wrapping the setValue
call in a ReactDOM.flushSync
. In this way, the operation will not be batched and we get a similar behaviour as of React 17
const [{ value }, , { setValue }] = useField(name);
useEffect(() => {
if (value === null)
flushSync(() => setValue("now it is not null"));
}, [value, fields, setValue]);
@marcoromag Thanks for sharing! It would be good that this issue is addressed by the Formik maintainers though.
@marcoromag I get Warning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.
with the workaround. How about you?
I downgraded to React 17 just to ensure myself this is really related to. And it is! Nonetheless because Formik encapsulates its state handling, Formik users can't do much about this without the help of Formik maintainers.
For what it's worth I fixed this by wrapping the call to formik.setFieldValue
in a setTimeout
to avoid the flushSync warning that @matths mentioned.
Sorry, but when you need to trick a framework or library using setTimeout, well, I would call this a hack. In my opinion, Formik is still heavily used and there are still people starting to use it right now, but they should not or they should be aware that they will go with a fork to keep this up to date with future React. At least for my project, we switched to React Hook Form, which is, as of now, a very active project, with fewer open issues or pull requests. It would be more then fair, when @jaredpalmer would put a disclaimer into the README that there's no active development right now. Maybe I am wrong, but the last release is from June 2021.
Hi! Are there any updates regarding this issue? I've ran into this issue 2.2.6
and it seems to be relevant on the latest patch too.
Also, to add to the workaround conversation, it seems to me that this issue only affects setValue
of useField
hook, but doesn't affect useFormikContext
's setFieldValue
. I managed to refactor code to use setFieldValue
and everything works as expected.
In any case this should be noted for anyone trying to upgrade to React 18, that this kind of issue can happen and it can hang the whole website. Pretty dangerous
I am having the same issue, using useFormik
like this
const formik = useFormik()
useEffect(() => {
formik.setFieldValue('field', value)
}, [formik])
Using setFieldValue like this removes the infinite loop
const {setFieldValue, ...formik} = useFormik()
useEffect(() => {
setFieldValue('field', value)
}, [setFieldValue])
I also noticed that calling setValues inside useEffects leads to infinite loop, however if you call setValues with disabled validation, then the there is no loop.
const { data } = useQuery(someQuery);
useEffect(() => {
if(data) {
formik.setValues(
mapDataToFormValues(data),
false // <-- fixes infinite loop
);
}
}, [data, formik]);
Bug report
Calling
helpers.setValue
results in an infinite loop when it's called inside useEffect in React 18.React 17: https://codesandbox.io/s/formik-usefield-hook-react-18-bug-forked-ucpg4d?file=/src/App.js
React 18: https://codesandbox.io/s/formik-usefield-hook-react-18-bug-nr052g?file=/src/App.js
The code of the
App
component is exactly the same on both sandboxes, what changes is the React major version.Expected behavior
React 17: If you click on the
CLICK
button in the React 17 sandbox, you will see that the input value will change fromaaa@mail.com
tobbb@mail.com
🆗 ✅Current Behavior
React 18: If you click on the
CLICK
button in the React 18 sandbox, it will cause an infinite loop ⚠️❗Suggested solution(s)
I don't know exactly what could be the root cause of this issue, but I suspect that it's related to the Automatic Batching feature/breaking change introduced by React 18:
https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#automatic-batching
What happens in practice is that the reducer for
SET_FIELD_VALUE
is never called for some reason, even though it's action gets dispatched within thesetFieldValue
function. The reducer forSET_ISVALIDATING
is executed instead.On React 17:
setFieldValue
is called;SET_FIELD_VALUE
SET_FIELD_VALUE
is executedvalidateFormWithHightPriority
is then executed ✅On React 18:
setFieldValue
is called;SET_FIELD_VALUE
validateFormWithHightPriority
is executed ❗SET_ISVALIDATING
is executedSET_FIELD_VALUE
is never executed for some reason ⚠️The infinite loop is then caused by the
useField
hook always returning the old value instead of the new value that was set throughhelpers.setValue(value)
.Your environment