2

这是背景.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;
}

这是它的样子: 在此处输入图像描述

4

1 回答 1

2

div.backdrop每次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])

编辑

  1. 我删除了存在的检测window,因为useEffect只会在客户端执行。

  2. 由于ReactDOM.createPortal将创建div.backdropHTMLElementdiv#next)的外部,我认为只需nullBackdrop组件中返回就可以了。

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;
于 2021-01-21T07:06:29.317 回答