-
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
ReactDOM.render()/unstable_renderIntoContainer() doesn't return instance if called during an update #10309
Comments
Seems like the same issue as #10294 (comment). |
This does seem related. I posted the small test case here that errors for me: https://github.com/jlongster/fiber-modal-error In this case the react-modal library is calling I was going to file a new bug but this one seems like it? |
Although it is weird because my above test cases works if you always render |
In your example, it only fails to get the instance on first render. This is why it only fails with |
@gaearon Yes, it def looks related I was working on a reproducible example too, but this isn't a trivial exercise trying to extricate only the relevant bits Interestingly everything renders correctly as it did before, its just not returning an instance (which is important for actions down the line) |
Can you please provide example? I just recreated your example in fiddle and it does return an object. |
I will file a new bug with mine because it might not be related, feel free to dupe to this one. |
@gaearon Great, ok - I'll take a look now I actually came to the same point that @jlongster did in #10294 - containerFiber.child is null in getPublicRootInstance I'll take a look at your example and try see what's different to mine - thanks |
I reproduced it! This only happens when |
Ah, great! I'll continue to see what I can find in the meantime though, but I am pleased you've managed to reproduce it! |
I filed my bug here: #10310 Still not sure if this is related. I think If we render it like |
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 |
This sounds correct to me. I’m not sure how to fix this. |
This is also slightly related to #8830, as we’d bump into this problem in async mode anyway. |
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? |
@sebmarkbage if portals are the way forward for this sort of thing, where can I find documentation/information on how these might be used? Alternatively, is there a way to get the instantiated component later? In our particular use case we don't necessarily need it immediately - some point later might be acceptable |
render() {
return ReactDOM.createPortal(MyComponent, domNode);
} You can mix that with regular elements too, e.g. <div>
<button />
{ReactDOM.createPortal(<MyComponent />, domNode)}
</div> |
There technically is although it's a bit weird. ReactDOM.render(<MyComponent />, node, function() {
console.log(this); // instance
}); Note that this won’t work with arrow functions. Or more familiar: ReactDOM.render(<MyComponent ref={instance => {
if (instance) {
// do something
}
}} />, node); |
Ok, I tried the portal approach and this does return something, but it isn't the instance I expect here. Perhaps I can describe my usecase - we develop a grid/table library whereby users can supply React components which our library then creates dynamically at runtime and inserts to render within the grid. Optionally users can then access these components at runtime and invoke methods on them (via an api we expose). For example, a user might supply a ToggleColorComponent with a method called "toggle" - they can get the instance of ToggleColorComponent for a given cell and invoke the toggle method (typically via an external input such as a button etc). Does this make sense? |
@seanlandsman If you don't need the component to be visible yet, I'd recommend doing If you do need to be visible at the same time, then I'd recommend using the Portal. The tricky part of the Portal API is if you want to render into something that you're also rendering. In that case, I'd recommend manually creating the container DOM node and appending the instance in class MyComponent extends Component {
myContainer = document.createElement('div');
attachPortal = (parent) = {
if (parent) {
parent.appendChild(this.myContainer);
}
}
render() {
return [
<div ref={this.attachPortal} />,
ReactDOM.unstable_createPortal(<MyOtherComponent />, this.myContainer)
];
}
} |
The second option described above (using hte callback) works for me - I had actually tried this but as I used an arrow function this hadn't worked. Trying a normal function as you described @gaearon did the trick. @sebmarkbage We do actually need the component to be visible when we render it, it's just that we don't necessarily need access to the instantiated component immediately, just at some point after (maybe even really really soon afterwards, but not necessarily immediately) If I use the callback approach described above (which works fine for me), is this likely to be a supported mechanism going forward? It suits us fine, but what we're trying to do here is ensure forward compatibility for when fiber is released thanks all for your help! |
@seanlandsman Just note that that technique doesn't work as well with async rendering as Portals. So it's not as future proof. |
Ok, I understand, thanks - close but no cigar |
I'm assuming you can do For the |
The original plan was to special case this and pre-render just one level deep. E.g. just instantiate the class. However, that turned out to not be that useful distinction. Because what you really want is the whole tree to be rendered so that you can safely call So I don't see a way to fully fix this without making it reentrant. However, that's a big architectural change that goes in the opposite direction of where we want to go (which is fully async). |
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 lifecycle method eg: componentDidMount). see: facebook/react#10309 (comment) Test plan: * when rendered into canvas-lms using react16, it shouldn’t throw errors
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 lifecycle method eg: componentDidMount). see: facebook/react#10309 (comment) Test plan: * when rendered into canvas-lms using react16, it shouldn’t throw errors
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/).
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
The text was updated successfully, but these errors were encountered: