New in 18: useEffect fires synchronously when it's the result of a discrete input #128
acdlite
announced in
Announcement
Replies: 1 comment 9 replies
-
Dumb question. Could you argue that in any case where you need this guarantee:
...you should just be using Or in other words, what happens if you somehow manage to submit the form before this |
Beta Was this translation helpful? Give feedback.
9 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
In React 18, useEffect fires synchronously when it's the result of a discrete input.
For example, if useEffect attaches an event listener, the listener is guaranteed to be added before the next input.
The same behavior applies to flushSync: the results of the effect are applied immediately, before the flushSync call exits.
The behavior for non-discrete events is unchanged: in most cases, React will defer the effect until after paint (though not always, if there's remaining time in the frame).
Note that this only affects the timing of when useEffect functions are called. It does not affect the priority of updates that are triggered inside a useEffect. They still get the default priority. (This is in contrast to useLayoutEffect, which not only calls the effect callback synchronously but also gives synchronous priority to its updates.)
Background
A discrete input is a type of event where the result of one event can affect the behavior of the next, like clicks or presses. Multiple discrete events cannot be batched or throttled without affecting program behavior.
A practical example where this matters is a counter. If the user increments a counter multiple times in quick succession, we must process each one individually so that the final count is correct.
In React 17 and below, the function passed to useEffect typically fires asynchronously after the browser has painted. The idea is to defer as much work as possible until paint so that the user experience is not delayed. This includes things like setting up subscriptions.
However, although passive effects do not block the browser from painting, they need to fire before the next discrete input event, so that the result can be observed by the event system.
For example, imagine you're building a form that disables "submit" after the first submission:
If the user attempts to submit the form multiple times in quick succession, we need to make sure the effects from the first event have completely finished before the next input is processed. So we synchronously flush them.
We don't need to do this for events that aren't discrete, because we assume they are not order-dependent and do not need to be observed by external systems.
Beta Was this translation helpful? Give feedback.
All reactions