无法让 Popover 在对话框中的正确位置显示

Posted

技术标签:

【中文标题】无法让 Popover 在对话框中的正确位置显示【英文标题】:Can't get Popover to display in correct position in Dialog 【发布时间】:2020-02-17 22:46:40 【问题描述】:

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

我已将我的代码简化为对话框中的一个简单按钮和弹出框,但仍然遇到同样的问题,并且对接下来要尝试的内容一无所知。我在控制台中得到的错误是

[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 警告,但仍然相对于页面而不是元素显示。有什么想法吗?

【问题讨论】:

我的猜测是,当您渲染 Popover 时,您的代码中的某些内容会导致按钮重新安装,因此触发 Popover 的 DOM 元素已被替换为新元素anchorEl 不再有效,但没有看到你的代码就很难确定。 这就是问题所在 - ListItem 在更改状态以显示 Popover 时重新渲染。一旦我通过 Popover 中的更改值导致 ListItemPopover 完全重新渲染。经过一些重组的乐趣,现在一切都很好。感谢您的提示。 【参考方案1】:

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

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

第二 - React.memo 可以防止对功能组件进行不必要的重新渲染(它仅在 props 不变的情况下才有效,但仍应在子组件中获得性能优势)。

【讨论】:

【参考方案2】:

    检查是否有display: none;样式

    可能是anchorEl用于多个嵌套功能组件问题

    尽量使用备忘录概念来防止组件重新渲染

【讨论】:

【参考方案3】:

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

【讨论】:

【参考方案4】:

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

所以我的主要功能组件只是返回了类似这样的内容

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 作为嵌套组件而不是组件内的函数调用时,React 会重新渲染丢失状态的弹出框导致状态丢失?我认为这与 javascript 在函数内封装方面的工作方式有关。

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

【讨论】:

以上是关于无法让 Popover 在对话框中的正确位置显示的主要内容,如果未能解决你的问题,请参考以下文章

不正确的 Popover 大小 - 在 Popover 中的 Navigation Controller 下显示 UITableViews 时

添加到 UITableViewCell 的 contentView 的 UITextField 在 Popover 中无法正确显示(iPad、iOS 4.2)

如何设置 Popover 视图以正确关闭

在显示 jQuery Mobile 页面之前让 Google 地图正确加载

从popover返回后如何在collectionview单元格中填充文本字段

bootstrap 中的popover放在input上怎么使用