Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't get Popover to display in correct position in Dialog

I have a Dialog and have a ListItem that when you click on it goes into edit mode by showing a Popover. This was working in an older version of MUI using a Modal but since getting on the latest that didn't work and I'm trying to use a Popover. I tried to make a simple example on CodeSandox but that works. What happens is the Popover is always in the upper left of the page instead of the ListItem.

I have simplified my code to a simple Button and Popover in the Dialog and still have the same problem and have ran out of ideas on what to try next. The error I get in the console is

[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.

When the item is clicked I do event.currentTarget just like in the examples and this is what the console.log looks like for it.

[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>

I even tried doing disablePortal in the Dialog which didn't fix it. I also tried using refs which fixed the anchorEl warning but still displays relative to the page and not the element. Any ideas?

like image 387
Mike Suiter Avatar asked Oct 21 '19 19:10

Mike Suiter


3 Answers

For anyone that comes across this issue with Material UI, there are a couple of things that you can do.

One is to make sure that if you have multiple nested functional components, that your anchorEl / click handlers for the popover are defined within the specific functional component that holds the popover. If you have nested functional components and the parent component holds the state, it will rerender the children on every state change, which can reset the anchorEl reference.

Second - React.memo can prevent unnecessary rerenders on functional components (it only works if props don't change but should still reap performance benefits in child components).

like image 123
jacobedawson Avatar answered Oct 07 '22 07:10

jacobedawson


  1. Check if there is any display: none; style

  2. May be anchorEl used in multiple nested functional components problem

  3. Try to use memo concept to prevent component rerender

like image 1
Kirubakaran R Avatar answered Oct 07 '22 07:10

Kirubakaran R


I have nested elements this is how I solved this without doing anything too extra.

So my main functional component simply returned something like this

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>
  )
}

I changed that line to {FilterButtons()} instead. It looks like the component that is rendering the popover needs to exist within the functional component calling it. However any nested components under do not need to be declared within the calling functional component.

From what I have gathered in looking at many peoples solutions is to use React.memo for this but this worked for me. Something about React re-rendering the popover losing the state when its called as a nested component rather than a function within the component causes the state to be lost? I assume it has to do with how JavaScript works in terms of encapsulation within a function.

I know this is an older question but I know people will still run by this question eventually.

like image 1
mjwrazor Avatar answered Oct 07 '22 08:10

mjwrazor