question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Provide a way to perform a synchronous render into another root during the commit phase

See original GitHub issue

Do 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:closed
  • Created 6 years ago
  • Reactions:3
  • Comments:13 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
ultimatecoolstarcommented, Nov 25, 2019

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 }

3reactions
saranrapjscommented, May 24, 2018

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:

  • create a HOC with a ref, that denotes the contentDOM insertion point
  • when the NodeView renders, manually create a DOM node to give to ProseMirror for the contentDOM, and insert this DOM node as a child of whichever root/parent node you’re rendering the React element into
  • listen for the React.render callback, and replace the HOC-rendered node that has the ref with the artificially created contentDOM 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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How did I re-render: Eliminate unnecessary React Effects
React's functionality is split into two phases, namely: Render, & Commit phase lifecycles, which run synchronously and update the UI. During the 'render' ......
Read more >
A Closer Look at ReactDOM.render - The Need to Know and ...
During the commit phase, the algorithm first calls the required lifecycle methods before updating or unmounting a component. Then, it performs ...
Read more >
Update on Async Rendering – React Blog
The solution to this problem is to use the new “commit” phase lifecycle, getSnapshotBeforeUpdate . This method gets called immediately before ...
Read more >
Is setState Executed Synchronously or Asynchronously in ...
This performSyncWorkOnRoot is the entry point for rendering, as mentioned before, it performs the render phase first, converting vdom to fiber, and then ......
Read more >
Inside Fiber: in-depth overview of the new reconciliation algorithm in ...
We'll take a detailed look at how React updates state and props and processes children. ... React performs work in two main phases:...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found