react: ReactDOM.render()/unstable_renderIntoContainer() doesn't return instance if called during an update
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
ReactDOM.render and ReactDOM.unstable_renderSubtreeIntoContainer no longer return created React component instances
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/84v837e9/).
this.eParentElement = document.createElement('div');
const ReactComponent = React.createElement(this.reactComponent, params);
this.componentRef = ReactDOM.render(ReactComponent, this.eParentElement);
What is the expected behavior?
After the steps above this.componentRef should be an instance just created - it is now null with React 16 beta.
It’s entirely possible that I should be doing something different now, but if so it’s not clear what that should be
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16 beta Chrome OSX
thanks
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 2
- Comments: 27 (8 by maintainers)
Commits related to this issue
- fix: support react@16 — committed to react-component/m-dialog by silentcloud 7 years ago
- Handle react 16’s async rendering this is a workaround for react 16 since ReactDOM.render is not guaranteed to return the instance synchronously (especially if called within another component's life... — committed to instructure/tinymce-a11y-checker by deleted user 6 years ago
- Handle react 16’s async rendering this is a workaround for react 16 since ReactDOM.render is not guaranteed to return the instance synchronously (especially if called within another component's life... — committed to instructure/tinymce-a11y-checker by deleted user 6 years ago
You can mix that with regular elements too, e.g.
I reproduced it! https://jsfiddle.net/x7c7bdh0/
This only happens when
ReactDOM.renderis called during an update (e.g. incomponentDidMount).There technically is although it’s a bit weird.
Note that this won’t work with arrow functions.
Or more familiar:
I believe the issue is related to how Fiber tracks scheduled work. Specifically: https://github.com/facebook/react/blob/master/src/renderers/shared/fiber/ReactFiberScheduler.js#L1408-L1420
When the root
ReactDOM.rendercall is made, no work is scheduled soperformWorkis called, which eventually sets thechildproperty on the Fiber. But whenReactDOM.renderis called incomponentDidMount, there’s already work in progress. So that!isPerformingWorkcheck evaluates tofalseand the work isn’t scheduled. That meansperformWorkisn’t called andchildis never set.This sounds correct to me. I’m not sure how to fix this. I think @acdlite should have the most context on this.
We currently force top level renders into a synchronous mode for compatibility with this case normally. As a legacy mode.
The core issue is that we don’t want to support reentrant renders. That’s why render is not synchronous in life-cycle methods. It’ll return what has already been rendered, which initially won’t be anything yet.
The idea is that these use cases should ideally switch to using Portals instead. Perhaps we should warn about these use cases and recommend switching to Portals?