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
here is the code that I use in playground
link: https://codesandbox.io/s/my-test-adapters-forked-z2qxfk
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>;
};
console output
index.js:27 render app true
14:28:49.848 index.js:27 render SomeComponent
14:28:49.849 index.js:27 created MView
14:28:49.849 index.js:27 create root <div class="reactToMarionette">…</div>
14:28:49.849 index.js:27 MView render
14:28:49.850 index.js:27 render ReactChild
14:28:51.846 index.js:27 render app true
14:28:51.846 index.js:27 render SomeComponent
14:28:51.846 index.js:27 created MView
14:28:51.846 index.js:27 create root <div class="reactToMarionette">…</div>
14:28:51.847 index.js:27 MView render
14:28:51.847 index.js:27 render ReactChild
14:28:51.847 index.js:27 render app false
14:28:51.847 index.js:27 MView destroy in useCallback reactToMarionette
14:28:51.848 index.js:27 onBeforeDestroy ReactDOMRoot
14:28:51.848 index.js:27 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.
at App (https://ocdkn0.csb.app/src/_app.js:31:41)
in App