this is backdrop.tsx:
interface BacdropProps {
open?: string;
onClick: () => void;
}
const Backdrop: React.FC<BacdropProps> = (props) => {
let container: HTMLDivElement | null = null;
if (typeof window !== "undefined") {
const rootContainer = document.createElement("div");
const parentElem = document.querySelector("#__next");
parentElem?.insertAdjacentElement("afterend", rootContainer);
// parentElem?.after(rootContainer) this gives me same issue
container = rootContainer;
}
return container
? ReactDOM.createPortal(
<div
className={["backdrop", props.open ? "open" : ""].join(" ")}
onClick={props.onClick}
/>,
container
)
: null;
};
export default Backdrop;
this is css for Backdoor.tsx
.backdrop {
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.75);
z-index: 100;
position: fixed;
left: 0;
top: 0;
transition: opacity 0.3s ease-out;
opacity: 1;
}
Your code will create div.backdrop
every time when Backdrop
re-render. The correct way should be create it once. The correct way is using useEffect
to promise ReactDOM.createPortal
just be executed once. And also apply the useRef
to make sure container
to keep the same instance in every render.
const containerRef = useRef<HTMLDivElement>(null);
useEffect({
// Will be execute once in client-side
if (typeof window !== "undefined") {
const rootContainer = document.createElement("div");
const parentElem = document.querySelector("#__next");
parentElem?.insertAdjacentElement("afterend", rootContainer);
// parentElem?.after(rootContainer) this gives me same issue
containerRef.current = rootContainer;
}
}, [window])
useEffect({
// Will be execute once when containerRef is bind to <HTMLDivElement>
if(containerRef.current) {
ReactDOM.createPortal(
<div
className={["backdrop", props.open ? "open" : ""].join(" ")}
onClick={props.onClick}
/>,
containerRef.current
)
}
}, [containerRef])
Edit
I removed the detection of existence in window
, since useEffect
would be executed only in client-side.
Since ReactDOM.createPortal
will create the div.backdrop
outside of the root HTMLElement
(div#next
), i think just return null
in Backdrop
component is fine.
const containerRef = useRef<HTMLDivElement>(null);
useEffect({
// useEffect would run just in client-side
const rootContainer = document.createElement("div");
const parentElem = document.querySelector("#__next");
parentElem?.insertAdjacentElement("afterend", rootContainer);
// parentElem?.after(rootContainer) this gives me same issue
containerRef.current = rootContainer;
}, [])
useEffect({
// Will be execute once when containerRef is bind to <HTMLDivElement>
if(containerRef.current) {
ReactDOM.createPortal(
<div
className={["backdrop", props.open ? "open" : ""].join(" ")}
onClick={props.onClick}
/>,
containerRef.current
)
}
}, [containerRef])
return null;