无法让 Popover 显示在 Dialog 中的正确位置

IT技术 reactjs material-ui
2021-05-15 11:41:29

我有一个对话框和一个 ListItem,当你点击它时,它会通过显示一个弹出框进入编辑模式。这是在使用 Modal 的旧版 MUI 中工作的,但自从获得最新版本后不起作用,我正在尝试使用 Popover。我试图在 CodeSandox 上做一个简单的例子,但有效。发生的情况是 Popover 总是在页面的左上角而不是 ListItem。

我已经将我的代码简化为对话框中的一个简单的 Button 和 Popover,但仍然存在同样的问题,并且对接下来要尝试的内容没有想法。我在控制台中得到的错误是

[Warning] Material-UI: the `anchorEl` prop provided to the component is invalid.
The anchor element should be part of the document layout.
Make sure the element is present in the document or that it's not display none.

单击该项目时,我会像示例中那样执行 event.currentTarget,这就是 console.log 的样子。

[Log] <button class="MuiButtonBase-root MuiButton-root MuiButton-text" tabindex="0" type="button"> (main.chunk.js, line 26437)
<span class="MuiButton-label">Click Me</span>
<span class="MuiTouchRipple-root">
<span class="MuiTouchRipple-ripple MuiTouchRipple-rippleVisible" style="width: 117.2006825918689px; height: 117.2006825918689px; top: -34.60034129593445px; left: -25.60034129593445px;">
<span class="MuiTouchRipple-child MuiTouchRipple-childLeaving"></span>
</span>
</span>
</button>

我什至尝试在没有修复它的对话框中执行 disablePortal。我还尝试使用 refs 修复了 anchorEl 警告,但仍然相对于页面而不是元素显示。有任何想法吗?

4个回答

对于在 Material UI 中遇到此问题的任何人,您可以做几件事。

一种是确保如果您有多个嵌套的功能组件,则您的弹出框的 anchorEl/click 处理程序是在包含弹出框的特定功能组件中定义的。如果您有嵌套的功能组件并且父组件保存状态,它将在每次状态更改时重新渲染子组件,这可以重置 anchorEl 引用。

其次 - React.memo 可以防止对功能组件进行不必要的重新渲染(只有在 props 没有改变但仍然应该在子组件中获得性能优势时才有效)。

  1. 检查是否有任何display: none;样式

  2. anchorEl用于多个嵌套函数组件问题

  3. 尝试使用备忘录概念来防止组件重新渲染

也有可能由于它所说的内容而出现此错误 - 您可能正在尝试使用具有display: none样式的元素作为组件的 achorEl,这不受支持,因为计算锚元素位置的底层逻辑需要它在屏幕上可见。

我嵌套了元素,这就是我在不做任何额外工作的情况下解决这个问题的方法。

所以我的主要功能组件只是返回了这样的东西

const filters = () => {
  const [anchorEl, setAnchorEl] = useState(null)
  const popoverOpen = Boolean(anchorEl)
  const handleTogglePopover = (event: any) => {
    event.preventDefault()
    if (anchorEl === null) {
      setAnchorEl(event.currentTarget)
    } else {
      setAnchorEl(null)
    }
  }
  const SortActions = () => {
    return (
      <Box>
        <MyCustomRadioButton/>
      </Box>
    )
  }
  const FilterButtons = () => {
    return (
      <Box>
        <ButtonBase
          onClick={handleTogglePopover}
          aria-owns={popoverOpen ? 'my-popover-id-name' : undefined}
          aria-haspopup={true}
        >
          {/* contents (this is a comment in html in react)  */}
        </ButtonBase>
        <Popover
          id={'my-popover-id-name'}
          open={popoverOpen}
          anchorEl={anchorEl}
          anchorOrigin={{
            vertical: 'bottom',
            horizontal: 'left'
          }}
          transformOrigin={{
            vertical: 'top',
            horizontal: 'left'
          }}
          onClose={handleTogglePopover}
        >
          <SortActions/>
        </Popover>
      </Box>
    )
  }
  return (
    <Box>
      {/* THIS LINE IS THE LINE I CHANGED TO GET IT TO WORK */}
      <FilterButtons/>
    </Box>
  )
}

我把那条线{FilterButtons()}改为。看起来呈现弹出窗口的组件需要存在于调用它的功能组件中。然而,下面的任何嵌套组件都不需要在调用功能组件中声明。

从我收集的许多人的解决方案来看,为此使用 React.memo 但这对我有用。当它作为嵌套组件而不是组件内的函数调用时,React 重新渲染会丢失状态的弹出窗口会导致状态丢失吗?我认为这与 JavaScript 在函数内封装的工作方式有关。

我知道这是一个较旧的问题,但我知道人们最终仍会回答这个问题。