-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Bug: useEffect fires synchronously even though it is not the result of a discrete input #31567
Comments
I think you should wrap this part in asynchronous function and call that function sychronously because current code is blocking the rendering (common practices). Also, Haven't you seen enough codebase where Current code const sleep = (time) => {
const wakeUpTime = Date.now() + time;
while (Date.now() < wakeUpTime) {}
console.log(time + "ms passed");
};
// and
useEffect(() => {
if (text === "Bonjour") {
// "Bonjour" text will have to wait for 10000 ms to be shown on screen
sleep(10000);
}
}, [text]);
Change this to something like this : const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
// and
useEffect(() => {
const handleBonjourEffect = async () => {
if (text === "Bonjour") {
await sleep(10000); // Allow rendering before this delay
console.log("10,000ms passed");
}
};
handleBonjourEffect();
}, [text]); |
That's not the point 🙂 . The sleep function is just a simulation representing some heavy task. If you use React 17, you can usually see the text "Bonjour" appear right after Screen.Recording.2024-11-19.at.03.31.56.movIt's related to this: The example with React 18 happens because the effect is fired synchronously before layout and paint (before the browser paints the updated screen). From React doc:
The point is why a setState inside a setTimeout (not from an interaction like a click) also causing the effect to run synchronously before paint like in the example? |
It is firing synchronously because it is |
Thank you for the report. With React 19, we'll consistently treat updates by default as sync. So unless they're triggered by a non-discrete user input (e.g. mousemove) or are wrapped in However, there's currently a bug where we don't yield to the browser before flushing passive effects from a concurrent render. That's why wrapping the update in |
Bug |
useEffect
fires synchronously and blocks painting new UI to screen even though it is not the result of a discrete input.React version: 18.3.1
Steps To Reproduce
Link to code example:
https://codesandbox.io/p/sandbox/7zqlmm
The current behavior
After
setText("Bonjour")
from asetTimeout
, it has to wait for 10000ms to be shown on screen.The expected behavior
After
setText("Bonjour")
from asetTimeout
, it's shown immediately on screen, as the doc says React only flush synchronously useEffect resulting from discrete events like clicks.Reference:
I don't know if this is a bug or an undocumented behavior. How is the
setState
insidesetTimeout
a discrete input ?The text was updated successfully, but these errors were encountered: