ReactDOM.createPortal
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReactDOM.createPortal相关的知识,希望对你有一定的参考价值。
参考技术A createPortal 的调用方式是:第一个参数是一个 renderable React child
第二个参数是一个DOM元素
将index.html页面添加DOM节点来验证createPortal如何渲染
大白话的意思是:
通过createPortal渲染的元素会被添加到另外的节点,同时点击事件会被触发;
而通过ReactDOM.render渲染的元素添加到新节点,但是点击事件没有触发。
ReactDOM.createPortal() 在 next.js-typescript 中创建额外的空白 div
【中文标题】ReactDOM.createPortal() 在 next.js-typescript 中创建额外的空白 div【英文标题】:ReactDOM.createPortal() is creating extra blank divs in next.js-typescript 【发布时间】:2021-04-25 12:46:22 【问题描述】:这是背景.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;
这是 Backdoor.tsx 的 css
.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;
这是它的外观:
【问题讨论】:
【参考方案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
,我认为只需在Backdrop
组件中返回null
即可。
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;
【讨论】:
感谢您的回答。我有 2 个问题。我们为什么要使用 [window]。目前它说“窗口”没有定义。看起来它是在服务器上呈现的。第二期,我要在组件中返回什么? 因为我的问题是询问如何解决额外 div 的问题,而您处理了这个问题。我接受了你的回答并喜欢这个答案。但我有另一个组件模式,它安装在 dom 上,但它没有显示在屏幕上。你能查一下这个问题吗:***.com/questions/65914360/…以上是关于ReactDOM.createPortal的主要内容,如果未能解决你的问题,请参考以下文章