简体   繁体   English

为什么底层组件重新渲染时popper会跳到左上角?

[英]Why does popper jump to top-left corner when underlying component re-renders?

I am using the Material-UI Popper component (which in turn uses popper.js) to create a hovering toolbar.我正在使用 Material-UI Popper 组件(它又使用 popper.js)来创建一个悬停工具栏。 For the most part it is working well, except for one odd behavior:在大多数情况下,它运行良好,除了一个奇怪的行为:

  1. Select some text: the hovering toolbar appears above the text - as expected.选择一些文本:悬停工具栏出现在文本上方 - 正如预期的那样。
  2. Select any button in the toolbar: the appropriate action is performed.选择工具栏中的任意按钮:执行相应的操作。 However the toolbar jumps to the top-left corner of the window.但是工具栏会跳转到窗口的左上角。 See below.见下文。

在此处输入图片说明

You can try out this behavior in my Storybook - just select some text and click on one of the "T" buttons.您可以在我的 Storybook 中尝试这种行为 - 只需选择一些文本并单击“T”按钮之一。

The basic issue centers around positioning of the popper:基本问题集中在 popper 的定位上:

  1. When user selects some text, a fake virtual element is created and passed to the popper as an anchor element.当用户选择一些文本时,会创建一个假虚拟元素并将其作为锚元素传递给 popper。 Popper uses this anchorEl to position the hovering toolbar. Popper 使用这个anchorEl来定位悬停工具栏。 So far so good.到现在为止还挺好。
  2. When the user clicks on a button in the toolbar, the hovering toolbar jumps to the top-left of the window.当用户单击工具栏中的按钮时,悬停的工具栏会跳转到窗口的左上角。

I am guessing this is happening because the anchor element is somehow lost when the underlying component re-renders.我猜这是因为当底层组件重新渲染时锚元素以某种方式丢失了。 I don't know why, but this is just my theory.我不知道为什么,但这只是我的理论。 Can someone help me solve this issue?有人可以帮我解决这个问题吗?

The code that computes the anchorEl sits inside a React useEffect() .计算anchorEl的代码位于 React useEffect() I have made sure that the dependency list for the useEffect is accurate.我已经确保useEffect的依赖项列表是准确的。 I can see that when the toolbar jumps, the useEffect() is NOT being called, which means that anchorEl is not being recomputed.我可以看到,当工具栏跳转时,没有调用useEffect() ,这意味着没有重新计算anchorEl This leads me to believe that the toolbar should stay intact in its current position and not jump to (0,0).这让我相信工具栏应该保持其当前位置不变,而不是跳到 (0,0)。 But that's not happening :-(.但这并没有发生:-(。

Here's the useEffect() code inside the toolbar component.这是工具栏组件中的useEffect()代码。 You can find the full code in my repo .你可以在我的 repo 中找到完整的代码。 Any help would be much appreciated.任何帮助将非常感激。

useEffect(() => {
    if (editMode === 'toolbar') {
        if (isTextSelected) {
            const domSelection = window.getSelection();
            if (domSelection === null || domSelection.rangeCount === 0) {
                return;
            }
            const domRange = domSelection.getRangeAt(0);
            const rect = domRange.getBoundingClientRect();
            setAnchorEl({
                clientWidth: rect.width,
                clientHeight: rect.height,
                getBoundingClientRect: () =>
                    domRange.getBoundingClientRect(),
            });
            setToolbarOpen(true);
        } else {
            setToolbarOpen(false);
        }
    } else {
        setToolbarOpen(false);
    }
}, [editMode, isTextSelected, selection, selectionStr]);

I believe your domRange is no longer valid after toggleBlock does its work (due to dom nodes getting replaced), so getBoundingClientRect is no longer returning anything meaningful.我相信您的domRangetoggleBlock完成其工作后不再有效(由于 dom 节点被替换),因此getBoundingClientRect不再返回任何有意义的内容。

You should be able to fix this by redoing the work of getting the range within the anchorEl's getBoundingClientRect .您应该能够通过重做获取 anchorEl 的getBoundingClientRect范围内的工作来解决此问题。 Perhaps something like the following (I didn't try to execute it, so no guarantee that there aren't minor errors):也许类似于以下内容(我没有尝试执行它,所以不能保证没有小错误):

const getSelectionRange = () => {
  const domSelection = window.getSelection();
  if (domSelection === null || domSelection.rangeCount === 0) {
    return null;
  }
  return domSelection.getRangeAt(0);
};
useEffect(() => {
  if (editMode === "toolbar") {
    if (isTextSelected) {
      const domRange = getSelectionRange();
      if (domRange === null) {
        return;
      }
      const rect = domRange.getBoundingClientRect();
      setAnchorEl({
        clientWidth: rect.width,
        clientHeight: rect.height,
        getBoundingClientRect: () => {
          const innerDomRange = getSelectionRange();
          return innerDomRange === null
            ? null
            : innerDomRange.getBoundingClientRect();
        }
      });
      setToolbarOpen(true);
    } else {
      setToolbarOpen(false);
    }
  } else {
    setToolbarOpen(false);
  }
}, [editMode, isTextSelected, selection, selectionStr]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM