Open arshaw opened 4 years ago
I forgot to thank you guys. Preact is awesome and has helped my company a lot! I've just become a Supporter.
Thank you so much for the detailed description and the great sandbox! With it, it's easy to see what's going on :raised_hands:
Looks like react is re-queuing the component in the same update loop before flushing to the DOM. Relying on timing specific things is always tricky and that may change sometime in the future. At least various frameworks are exploring that path and at some point we may too.
I wonder if something that signals that rendering is done would suffice in your use case.
Thanks for taking a look at this @marvinhagemeister.
My users really like having all rendering completed by the synchronous execution of the very next line. Exposing a callback or promise API that fires after rendering is completed would not be the best for them.
Who knows, maybe if you do any refactoring of Preact's render queueing system in the future, this may be fixed for free.
Reproduction
The demo I've made consist of a simple component that calls
setState
from itscomponentDidUpdate
method, causing it to render again.With React: https://codesandbox.io/s/react-render-setstate-92o3d , in the console it outputs:
With Preact: https://codesandbox.io/s/preact-render-setstate-xm6gl?file=/src/index.js , in the console it outputs:
Expected Behavior
I would expect Preact to behave like React, executing a setState-induced rerender synchronously if the initial root render is still executing.
Actual Behavior
For Preact, it executes asynchronously, completing the second render after the initial render finishes.
Most React documentation says that
setState
(or its hooks equivalent) are executed asynchronously and that you should not depend on the timing. However, I've definitely read something in the past (written by Dan Abramov I think, though I can't find it now) that says that any state-setting that happens in the initial render executes synchronously. And this tidbit in the React docs might hint at that: "Currently, setState is asynchronous inside event handlers" ... in effect, anything that sets state dynamically, after the initial render, but not necessarily for the initial render.Regardless of how much a part of the React "spec" this is, and how much we want Preact to conform to React behavior, this synchronously-executing setState behavior is very useful. I am the author of a JavaScript widget that internally uses Preact, but exposes a vanilla JS API, and users of my widget depend on the initial rendering completing synchronously, so they can start interacting with the DOM right after.
If you are curious, I found a way to force-flush all pending renders to the DOM but it's really ugly and prone to breaking: https://github.com/fullcalendar/fullcalendar/blob/v5.1.0/packages/core-vdom/src/vdom-preact.ts#L41-L62