ReactDOM.createPortal() 在 next.js-typescript 中创建额外的空白 div
ReactDOM.createPortal() is creating extra blank divs in next.js-typescript
这是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;
这是 css 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;
}
这是它的样子:
您的代码将在每次 Backdrop
重新呈现时创建 div.backdrop
。正确的方法应该是创建一次。正确的方法是使用 useEffect
承诺 ReactDOM.createPortal
只执行一次。并且还应用 useRef
以确保 container
在每个渲染中保持相同的实例。
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])
编辑
我删除了 window
中存在的检测,因为 useEffect
只会在客户端执行。
由于 ReactDOM.createPortal
将在根 HTMLElement
(div#next
) 之外创建 div.backdrop
,我认为只是 return null
在 Backdrop
组件中是好的。
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;
这是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;
这是 css 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;
}
这是它的样子:
您的代码将在每次 Backdrop
重新呈现时创建 div.backdrop
。正确的方法应该是创建一次。正确的方法是使用 useEffect
承诺 ReactDOM.createPortal
只执行一次。并且还应用 useRef
以确保 container
在每个渲染中保持相同的实例。
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])
编辑
我删除了
window
中存在的检测,因为useEffect
只会在客户端执行。由于
ReactDOM.createPortal
将在根HTMLElement
(div#next
) 之外创建div.backdrop
,我认为只是 returnnull
在Backdrop
组件中是好的。
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;