For my app, I need to render some children, and then measure the resulting div. In pseudo code, it would look something like this:
function getDims(child) {
var testEl = document.getElementById('test-el');
ReactDOM.render(child, testEl);
var dims = testEl.getBoundingClientRect();
ReactDOM.unmountComponentAtNode(testEl);
return dims;
}
Unfortunately, according to the documentation ReactDOM.render may in the future bee asynchronous. Is there a future-proof option to force synchronous rendering so the above function will work?
For my app, I need to render some children, and then measure the resulting div. In pseudo code, it would look something like this:
function getDims(child) {
var testEl = document.getElementById('test-el');
ReactDOM.render(child, testEl);
var dims = testEl.getBoundingClientRect();
ReactDOM.unmountComponentAtNode(testEl);
return dims;
}
Unfortunately, according to the documentation ReactDOM.render may in the future bee asynchronous. Is there a future-proof option to force synchronous rendering so the above function will work?
Share Improve this question asked Oct 29, 2016 at 12:27 derekdreeryderekdreery 3,9225 gold badges31 silver badges41 bronze badges 1-
1
I imagine you could use react-dom-server with
renderToStaticMarkup
orrenderToString
and insert that string into the DOM afterwards. Of course it's a bit sad to serialize into a string without the need for it. But ifasync
isn't an option it may just work. – Fiona Runge Commented Feb 1, 2018 at 10:36
3 Answers
Reset to default 5You can pass a callback to ReactDOM.render(ReactElm, DOMNode, callback)
. The callback
will be executed once the ReactElm
gets loaded into the DOMNode
.
Hope this helps!
function getDims(child) {
var testEl = document.getElementById('test-el');
ReactDOM.render(child, testEl, function(){
var dims = testEl.getBoundingClientRect();
ReactDOM.unmountComponentAtNode(testEl);
return dims;
});
}
Usage example:
class App extends React.Component{
render(){ return <h1>Hello</h1>}
}
ReactDOM.render(<App/>,
document.getElementById('app'),
() => console.log('Component Mounted'))
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
In React 18+, we can use renderToString
:
const view = renderToString(<div id="text-div-id">{text}</div>);
const outerEl = document.getElementById('outer-div-id') as HTMLDivElement;
outerEl.innerHTML = view;
const textEl = document.getElementById('text-div-id') as HTMLDivElement;
const bounding = textEl.getBoundingClientRect();
In React 18+, on the client you can use flushSync
:
import { createRoot } from 'react-dom/client';
import { flushSync } from 'react-dom';
const div = document.createElement('div');
const root = createRoot(div);
flushSync(() => {
root.render(<MyIcon />);
});
console.log(div.innerHTML); // For example, "<svg>...</svg>"
renderToString
or renderToStaticMarkup
are from react-dom/server
and importing it on the client unnecessarily increases your bundle size and should be avoided.
+ Read More