jaredpalmer / jaredpalmer/formik

helpers.setValue causes infinite loop when called inside useEffect in React 18

Open
#3,602 10 comments 19 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Type: Bug
Dominant language
TypeScript
Stars
34.3k
Forks
2.8k
PR merge metrics
No merged PRs in 30d

Description

## 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 from `aaa@mail.com` to `bbb@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 the `setFieldValue` function.
The reducer for `SET_ISVALIDATING` is executed instead.

On React 17:
- Formik's `setFieldValue` is called;
- dispatch `SET_FIELD_VALUE`
- Reducer for `SET_FIELD_VALUE` is executed
- `validateFormWithHightPriority` is then executed ✅

On React 18:
- Formik's `setFieldValue` is called;
- dispatch `SET_FIELD_VALUE`
- `validateFormWithHightPriority` is executed ❗
- Reducer for `SET_ISVALIDATING` is executed
- Reducer for `SET_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 through `helpers.setValue(value)`.

![Screenshot 2022-07-11 at 17 23 50](https://user-images.githubusercontent.com/99957910/178299763-534993e7-1ecb-453f-bbab-6693a66a5339.png)

![Screenshot 2022-07-11 at 17 23 33](https://user-images.githubusercontent.com/99957910/178299775-d07f09dd-bde0-4f29-be5b-efa7bda0c175.png)

![Screenshot 2022-07-11 at 17 24 45](https://user-images.githubusercontent.com/99957910/178299796-74e88fd1-a092-4e74-8ba8-50dc288f355e.png)

### Your environment

| Software | Version(s) |
| ---------------- | ---------- |
| Formik | 2.2.9
| React | 18.2.0
| TypeScript | -
| Browser | Chrome
| npm/Yarn | Yarn
| Operating System | Mac OS X

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked React 17 and React 18 CodeSandbox reproductions, then trace Formik's setFieldValue, useField, and reducer flow described in the report. Done means the React 18 example updates the input from aaa@mail.com to bbb@mail.com without entering an infinite loop, while preserving the React 17 behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.