Provide a way to perform a synchronous render into another root during the commit phase
See original GitHub issueDo you want to request a feature or report a bug?
Bug
What is the current behavior?
Reentrancy checks prevent synchronous ReactDOM.render
in a nested React component. This used to work before React 16, and seems related to this issue about nested ReactDOM renders
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn’t have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
Here’s a JSFiddle that documents the problem, with a simulation of the external dependency where this manifests.
What is the expected behavior?
I’m running into what I think is a similar problem to this one, with a nested ReactDOM.render
, except where the difference is that I don’t think we can use portals to address our use-case.
We have a component which manages the DOM tree for all nodes below it outside of React — it’s a contenteditable node and uses the best-in-class ProseMirror library to manage its children. The component looks something like this:
class ProseMirror extends Component {
componentDidMount() {
// ProseMirror manages the DOM for all nodes below this.el.
}
setRef(el) {
this.el = el;
}
render() {
return (<div ref={this.setRef} />);
}
}
As part of its render cycle, our configuration of ProseMirror ends up calling:
ReactDOM.render(<CrucialSubComponent />, someDivManagedByProseMirror);
to render an isolated child node of <ProseMirror />
, and wants to be able to immediately afterwards be able to leverage:
this.el.querySelector('.my-subcomponent')
…but this piece of the DOM is no longer available synchronously, and it looks like this is because of the re-entrancy change that came about in React 16. Portals don’t work for us, because the site where the ReactDOM.render
is being called isn’t itself directly part of the root React tree (this is clearer to observe in the fiddle). The hierarchy is something like <ProseMirror /> ---> (opaque ProseMirror rendering code) --> <CrucialSubComponent />
. Is there a way to skip these re-entrancy checks in these cases where there’s an isolated React render happening in a grandchild of a component, but where the React tree isn’t the immediate parent?
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
Querying the DOM immediately after ReactDOM.render worked in versions prior to React 16. We’re excited about the async possibilities for our main React tree, but curious if there are workarounds where we can ignore the reentrancy checks for these isolated renders.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:3
- Comments:13 (4 by maintainers)
We can use async await and callback function and make it blocking.
the code is 👍 https://jsfiddle.net/e5hbzc1r/14/ the above code is unsuccessful, but changing the code as below slightly makes it synchronous and successfully finds the element.
async function opaqueRenderProcess(el) { el.contentEditable=“true”; const div = document.createElement(‘div’); el.appendChild(div); await ReactDOM.render(< SubComponent />, div, async ()=>{ await console.log(“hello world here!!!”) } ); // outside libraries which used to rely on ReactDOM.render // operating synchronously can no longer do so: // e.g. this: console.log(el.querySelector(‘.insertion-point’)); // is null, which means that external libraries which depends on // the DOM being rendered synchronously here can no // longer do so }
My understanding of ProseMirror’s stance is that adding asynchronous rendering would over-complicate an already complicated state update -> view update logic. ProseMirror’s render cycle is view framework agnostic, and needs to know specific DOM positions synchronously in order to map DOM positions to pieces of the state (this is done, in part, so that DOM updates which occur by way of native browser behavior are interpreted correctly according to the last-known concept of the DOM).
@patsimm for what it’s worth, the workaround we were successful in making React 16 / ProseMirror play nicely for ProseMirror nodes with children:
contentDOM
insertion pointcontentDOM
, and insert this DOM node as a child of whichever root/parent node you’re rendering the React element intocontentDOM
node from step 2…this is a bit of a hack, but neither ProseMirror nor React seem to complain about it. Agreed that it would all be a little easier if it were still possible to guarantee synchronous render for nested React renders like this.