we get a warning on (legitimate?) use case, if there is a manually added react root inside another parent root
(for example for part of Backbone view that is inserted into parent react component)
on unmount we can see that child root is in commit context, so we cant unmount it
React version: 18.2
Steps To Reproduce
- have nested react roots, child is manually added/removed on parent mount/unmount
- unmount parent root
I have 2 way bindings for backbone.marionette and react
- reactToMarionette
- useMarionetteInReact hook
when root/parent component gets rendered/destroyed React gives this warning
Warning: Attempted to synchronously unmount a root while React was already rendering.
React cannot finish unmounting the root until the current render has completed,
which may lead to a race condition.
I am looking for ways to fix this warning
I think ReactChild node is somehow marked as toBeRendered at the app render, even though I would expect that app.root would not know about nested/inserted MView react root
Link to code example: https://codesandbox.io/s/my-test-adapters-forked-tdbgdb
problematic code
import { View } from "backbone.marionette";
import React, { useCallback, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
export function App() {
// create a reason to render inner component
const [isVisible, setVisible] = useState(true);
const toggle = useCallback(() => setVisible((i) => !i), []);
console.log("render app", isVisible);
return (
<>
<button onClick={toggle}>{`toggle: ${isVisible}`}</button>
{isVisible ? <SomeComponent /> : null}
</>
);
}
// component that children are controlled from outside (by marionette)
const SomeComponent = () => {
console.log("render SomeComponent");
const ref = useMarionetteInReact();
return <div ref={ref} className="stable-react-div"></div>;
};
// hook, for rendering marionette view
const useMarionetteInReact = () => {
const viewRef = useRef(null);
const divRef = useCallback((el) => {
if (el === null) {
console.log("MView destroy in useCallback", viewRef.current);
viewRef.current && viewRef.current.destroy();
} else {
console.log("created MView");
const MView = new reactToMarionette({
className: "reactToMarionette",
template: false,
component: <ReactChild />
});
viewRef.current = MView;
MView.render();
el.appendChild(MView.el);
}
}, []);
return divRef;
};
class reactToMarionette extends View {
constructor(options) {
super(options);
this.component = options.component;
this.el.textContent = "I am Marionette";
console.log("create root", this.el);
this.divEl = document.createElement("div");
this.divEl.classList.add("portal-root");
this.root = createRoot(this.divEl);
this.el.append(this.divEl);
}
render() {
console.log("MView render");
this.root.render(this.component);
}
onBeforeDestroy() {
console.log("onBeforeDestroy", this.root);
if (this.root) {
// setTimeout(() => this.root.unmount());
this.root.unmount();
}
}
}
const ReactChild = () => {
console.log("render ReactChild");
return <div> Hello, I am react child </div>;
};
The current behavior
warning is displayed
The expected behavior
no warning ?
we get a warning on (legitimate?) use case, if there is a manually added react root inside another parent root
(for example for part of Backbone view that is inserted into parent react component)
on unmount we can see that child root is in commit context, so we cant unmount it
React version: 18.2
Steps To Reproduce
I have 2 way bindings for backbone.marionette and react
when root/parent component gets rendered/destroyed React gives this warning
I am looking for ways to fix this warning
I think ReactChild node is somehow marked as
toBeRenderedat the app render, even though I would expect that app.root would not know about nested/inserted MView react rootLink to code example: https://codesandbox.io/s/my-test-adapters-forked-tdbgdb
problematic code
The current behavior
warning is displayed
The expected behavior
no warning ?