-
Notifications
You must be signed in to change notification settings - Fork 47.9k
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
componentDidUpdate fires before browser paint operation #2659
Comments
cc: @sebmarkbage |
For the record, one thing I quickly noticed when deferring post-render life cycle methods is that you can no longer execute code after ...I may have noticed this when I ran the test suite and pretty much every test that asserts a list of executed life cycle methods failed. This doesn't seem to be a possible bug so much as a philosophical decision on the expectations surrounding React's render methods and their corresponding life cycle methods. |
This is indeed intentional. React reconciliation life-cycle occurs within a single event loop. Because there's no clear scheduling API within an event loop, we rely on explicit batching which ensures that only one reconciliation happens within a batch. In those cases, React.render is not synchronous. Ideally it would always be asynchronous. The use case for Normally, all the writes you do on a DOM component has no effect until then next event loop anyway. Unless you read in between, but you have no reads in your example. You can explicitly schedule some work to happen after the next frame is painted, using Can you elaborate more on your particular use case? |
I totally agree with the reasoning you've laid out @sebmarkbage. I can give you some color on my particular scenario, which I also admit is something of an edge case. I am building a UI with a particular element that can have one of three CSS When the state of my UI dictates that this element will toggle from In order to get around this, the current solution is to:
As I found, because Thanks for replying, @sebmarkbage, and as far as I'm concerned, this issue can be closed. If you agree, I'll leave it to you to close it. |
@jackwanders would you mind sharing how you deferred the contents of componentDidUpdate? Did you use requestAnimationFrame? |
dom reflow will happen before didUpdate (so I can measure heights), and repaint happens after didUpdate (so i can effect based on the height without flicker)? |
I'm not sure that this isn't by design, but I discovered today that the lifecycle methods executed after a React element renders (i.e.
componentDidMount
andcomponentDidUpdate
, executed at the point noted below) appear to be executed before the browser's paint operation.src/browser/ReactReconcileTransaction.js#L81
This introduced some unexpected behavior in a scenario similar to this example:
Given the React class above, I would expect the following order of operations when the component re-renders in the browser:
componentWillUpdate
firesrender
firescomponentDidUpdate
firesHowever, this is not the case. The actual behavior is that steps 3 and 4 occur in the reverse order. In Chrome, when I record a timeline of events, I see the following:
Recalculate Style
Update Layer Tree
Paint x 3
Composite Layers
It this example, it looks like
this.getDOMNode().style.someValue
changes, then changes back before the paint ever occurs, rendering my efforts futile without manually deferring the logic incomponentDidUpdate
usingwindow.setTimeout
.To test my theory that the order would be different if
componentDidUpdate
fired after the paint operation, I made the following changes to the React source:https://github.com/jackwanders/react/compare/defer-post-render-lifecycle-methods?expand=1
When I load the same element in the browser using the modified code, the timeline in Chrome is as follows:
Recalculate Style
Update Layer Tree
Paint x 3
Composite Layers
Recalculate Style
This is the order of operations I would expect with the provided lifecycle methods defined in my example class above.
As I said, I can't say for certain that this is something that should be corrected, but I wanted to submit this issue and hopefully get some feedback from a React contributor, who is sure to have more domain knowledge and can explain why the existing flow exists, if indeed it is as designed.
Thanks.
The text was updated successfully, but these errors were encountered: