react icon indicating copy to clipboard operation
react copied to clipboard

Bug: First render doesn't create DOM nodes before next javascript is executed in script

Open bryceosterhaus opened this issue 1 year ago • 6 comments

This may have been discussed elsewhere but I wasn't able to find anything.

With the update to using createRoot in React 18, the DOM is created asynchronously, which means any code running after root.render() cannot depend on the DOM that React is creating.

React version: 18.2.0

Steps To Reproduce

const root = ReactDOM.createRoot(document.getElementById("app"));
root.render(
    React.createElement("div", { id: "reactChild" }, "Rendered By React")
);

document.getElementById("reactChild").innerHTML = "Replaced By VanillaJS"; // this errors

The current behavior

JS will error because document.getElementById("reactChild") is null and not found

The expected behavior

React will render first and then document.getElementById("reactChild") will execute find the node

Link to code example

Is this just an expected result with React 18+? If you fallback and use ReactDOM.render() instead, it works as expected.

bryceosterhaus avatar Sep 05 '24 09:09 bryceosterhaus

Hi @bryceosterhaus! I think this is expected in React 18+, but you can make it sync by using flushSync(() => ...).

Try this, it should work:

// REACT 18
const root = ReactDOM.createRoot(document.getElementById("app18"));
ReactDOM.flushSync(() =>
  root.render(
    React.createElement("div", { id: "child18" }, "THIS DOESN'T WORK")
  )
);

document.getElementById("child17").innerHTML = "THIS WORKS!";
document.getElementById("child18").innerHTML = "THIS WORKS!";

gianlucadifrancesco avatar Sep 05 '24 20:09 gianlucadifrancesco

You really shouldn't alter nodes created by React from the outside as it'll mismatch from the virtual dom. Any particular use case I'm missing?

Besides, flushSync forces React to render the tree in sync, which will definitely hurt performance at some point.

matvp91 avatar Sep 05 '24 20:09 matvp91

It isn't ideal, but in our use case it is possible that we would render part of the page with a shared React component and then a team or customer may want to write their own inline javascript that reads some data from the DOM that React created. The server then puts all these together on the same page. For example:

// Rendered by the platform
<script type="text/javascript">
    const root = //...
    root.render(<Button id="myButton" />);
</script>


// Added by another team/customer
<script type="text/javascript">
    const button = document.getElementById('myButton');
    // do something here with button...
</script>

Regardless, it still strikes me as odd that before/after React 18, this behavior is significantly different.

bryceosterhaus avatar Sep 06 '24 05:09 bryceosterhaus

Thanks @gianlucadifrancesco, that does work. I still find it an odd behavior though, especially because just reading through a script like my example above, it isn't intuitive that the element won't be created in time. I know React 18+ has async rendering now, but I didn't expect my script to continue executing without the DOM being created yet for that initial render.

bryceosterhaus avatar Sep 06 '24 05:09 bryceosterhaus

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Dec 05 '24 06:12 github-actions[bot]

bump

bryceosterhaus avatar Dec 05 '24 06:12 bryceosterhaus

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Mar 05 '25 08:03 github-actions[bot]

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

github-actions[bot] avatar Mar 12 '25 09:03 github-actions[bot]