React门户弹出窗口创建不匹配的instance

时间:2019-03-14 10:48:00

标签: javascript reactjs

更新

似乎每个新窗口/ iframe / etc等都有其自己的类集,因此instanceof不能工作,因为它们实际上不是同一实例。 不过,除了库不使用instanceof进行检查之外,尚不确定该解决方案。

TLDR :instanceof在看似相同的实例/代码上的行为不同。

我正在构建一个包含地图(地图框)的React后台界面,并希望添加一个功能以弹出地图,以便用户可以在单独的显示器上查看它。

使用React.createPortal,您可以将组件呈现到window.open元素中,效果很好。除了地图框。 问题不仅仅在于mapbox,mapbox会将其地图渲染到容器中,您在实例化地图时将此容器作为参数提供。 Mapbox检查此容器是字符串还是

instanceof HTMLElement

正常渲染组件时可以使用,但是将组件渲染到弹出窗口/门户时会失败,容器检查会突然失败。

我已经能够在codeandbox中复制它: https://codesandbox.io/s/qllpz89w39

问题 -是否有人知道导致此行为的原因? -有没有办法将容器“投射”到HTMLElement

源代码:

import React from "react";
import ReactDOM from "react-dom";

function App() {
  return (
    <div className="App">
      <h1>Weird bug</h1>
      <PortalTest t="Normal" />
      <WithPortal>
        <PortalTest t="In Portal" />
      </WithPortal>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

function WithPortal({ children }) {
  const div = document.createElement("div");
  const popupWindow = window.open(
    "",
    "test",
    "width=600px,height=640px,left=100,top=100,toolbar=no,menubar=no,location=no"
  );
  if (!popupWindow) {
    return (
      <div style={{ background: "red", padding: "16px", color: "white" }}>
        <h1>Popup blocked</h1>
        <p>Please allow popups for codesandbox to see the full script</p>
      </div>
    );
  }
  popupWindow.document.body.appendChild(div);
  return ReactDOM.createPortal(children, div);
}

function PortalTest({ t }) {
  const ref = React.useRef(null);
  const [value, setValue] = React.useState("idle");
  const [el, setEl] = React.useState(null);
  React.useEffect(
    () => {
      if (el) {
        if (el instanceof HTMLElement) {
          setValue("HTMLElement");
        } else {
          setValue("Unknown");
        }
      }
    },
    [el]
  );

  function setRef(x) {
    setEl(x);
  }

  return (
    <div ref={setRef}>
      <h3>{t}</h3>
      Found ref type: {value}
    </div>
  );
}

1 个答案:

答案 0 :(得分:0)

非常有趣,所以正如您自己说的那样,新窗口具有自己的一组类,而它们的引用不匹配。即:

window.HTMLElement !== popupWindow.HTMLElement

在您的codeandbox示例中,您可以更改检查内容,如下所示,您将看到条件将在弹出窗口中开始满足,并在主窗口中失败:

if (el instanceof popupWindow.HTMLElement) {
  setValue("HTMLElement");
} else {
  setValue("Unknown");
}

我的初衷是尝试重新分配popupWindow.HTMLElement

popupWindow.HTMLElement = HTMLElement

但是它没有用,所以我认为选项是:

  1. 实施一个单独的URL,该URL将返回带有mapbox的React应用,并通过window.open()打开它的URL
  2. 调整库instanceof HTMLElement的检查,以考虑到窗口上下文(您应该以某种方式将其传递到此处)

但是我相信正确的选择是选择#1,因为我真的不确定如果要实现这种跨窗口JS代码不会有其他麻烦。