4

For reasons too long to explain here (and not very relevant) I want to be able to circumvent React's render batching and force a component tree to be immediately rendered in some DOM element, even if it means a forced layout and a slowdown of the application.

I don't need any listeners bound - I just need to measure the dimensions of the rendered component and then remove it from the DOM.

The only solution I can think of is rendering to string and setting the innerHTML of the container, but the renderToString method is only available on react-dom/server and by looking in the code it seems that it changes the batching strategy globally in the entire React library.

Any ideas?

disc0dancer
  • 9,185
  • 11
  • 36
  • 46

2 Answers2

1

Accordingly to my tests, React.render seems to be sync. Take a look at this snippet:

class MyComponent extends React.Component {
  render(){ return <h1>Hello</h1>; }
}

const root = document.getElementById('root');

const testDiv = document.createElement('div');

root.appendChild(testDiv);

ReactDOM.render(<MyComponent />, testDiv);

document.getElementById('size').innerText = `${testDiv.clientWidth}px x ${testDiv.clientHeight}px`;
document.getElementById('inner-html').innerText = testDiv.innerHTML;

root.removeChild(testDiv);
<div id="root"></div>
<div>Size: <span id="size"></span></div>
<div>innerHTML: <span id="inner-html"></span></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
rosenfeld
  • 1,730
  • 15
  • 19
-2

I use the following (dirty) method to be able to maintain a synchronous flow within my application:

var doneRendering = false;

ReactDOM.render(component, container, function () {
    doneRendering = true;
});

while (!doneRendering) {
    console.log("Waiting");
}

console.log(container);

Instead you could also use a polling method like described here which essentially replaces the while loop with something like

var interval = setInterval(function() {
    if (doneRendering) {
        clearInterval(interval);
        console.log(container);
    }
}, 100);
Community
  • 1
  • 1
Corstian Boerman
  • 848
  • 14
  • 32
  • 1
    I haven't tried it but I'm pretty sure your first suggestion would simply freeze because React wouldn't have a chance to render due to the infinite while loop. Javascript is single-threaded (except when using workers). Your second method with setInterval is not sync as requested in the question. – rosenfeld Apr 13 '18 at 19:51
  • As mentioned by @rosenfeld, this method only works if `ReactDOM.render()` is already synchronous, otherwise it'll freeze the app. – kunnix Feb 18 '19 at 09:21