You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Material-UI数组生成多悬浮Popover仅最后一个可打开的问题排查

解决Material-UI多个Popover仅最后一个打开的问题

你遇到的问题核心是所有Popover共用了同一个状态变量——当前组件里的open和anchorEl是全局的,不管鼠标悬停哪个元素,都会把anchorEl设置为当前触发的元素,同时所有Popover的open都会变成true。但因为所有Popover都绑定了同一个anchorEl,最终只有最后一个能正确定位显示,其他的虽然open是true,但锚点不对,所以看不到。

要解决这个问题,我们需要给每个Popover维护独立的状态,具体步骤如下:

1. 修改状态结构

把原来单一的anchorEl改成对象,用每个元素的_id作为键,存储对应Popover的锚点元素;open状态可以通过判断对应ID的anchorEl是否存在来生成,不用单独存。

修改后的state初始化:

this.state = {
  anchorEls: {} // 键是元素_id,值是对应的锚点元素
};

2. 更新事件处理函数

让handlePopoverOpen和handlePopoverClose接收元素的_id,针对该ID更新状态:

handlePopoverOpen = (event, id) => {
  this.setState(prevState => ({
    anchorEls: {
      ...prevState.anchorEls,
      [id]: event.target
    }
  }));
};

handlePopoverClose = (id) => {
  this.setState(prevState => ({
    anchorEls: {
      ...prevState.anchorEls,
      [id]: null
    }
  }));
};

3. 调整render中的Popover属性

在map循环里,每个Popover的open和anchorEl都对应当前元素_id的状态:

{multi.map(m => (
  <li key={m._id}>
    <Typography 
      onMouseEnter={(e) => this.handlePopoverOpen(e, m._id)} 
      onMouseLeave={() => this.handlePopoverClose(m._id)}
    >
      {m.name}
    </Typography>
    <Popover 
      className={classes.popover}
      classes={{ paper: classes.paper }}
      open={!!this.state.anchorEls[m._id]} // 仅当前ID的锚点存在时打开
      anchorEl={this.state.anchorEls[m._id]} // 绑定当前ID的锚点
      anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
      transformOrigin={{ vertical: 'bottom', horizontal: 'center' }}
      onClose={() => this.handlePopoverClose(m._id)} // 加上Popover自身的关闭事件
    >
      <Typography>
        <a href={m.linkUrl} target="_blank" rel="noopener noreferrer">
          {m.hoverText}
        </a>
      </Typography>
    </Popover>
  </li>
))}

完整修改后的代码

class MultiplePopover extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      anchorEls: {}
    };
  }

  handlePopoverOpen = (event, id) => {
    this.setState(prevState => ({
      anchorEls: {
        ...prevState.anchorEls,
        [id]: event.target
      }
    }));
  };

  handlePopoverClose = (id) => {
    this.setState(prevState => ({
      anchorEls: {
        ...prevState.anchorEls,
        [id]: null
      }
    }));
  };

  render() {
    const { classes } = this.props;
    const multi = [
      { _id: 0, name: 'name1', hoverText: 'text1', linkUrl: '#' },
      { _id: 1, name: 'name2', hoverText: 'text2', linkUrl: '#' },
      { _id: 2, name: 'name3', hoverText: 'text3', linkUrl: '#' }
    ];

    return (
      <div className="wrapper">
        <ul>
          {multi.map(m => (
            <li key={m._id}>
              <Typography 
                onMouseEnter={(e) => this.handlePopoverOpen(e, m._id)} 
                onMouseLeave={() => this.handlePopoverClose(m._id)}
              >
                {m.name}
              </Typography>
              <Popover 
                className={classes.popover}
                classes={{ paper: classes.paper }}
                open={!!this.state.anchorEls[m._id]}
                anchorEl={this.state.anchorEls[m._id]}
                anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
                transformOrigin={{ vertical: 'bottom', horizontal: 'center' }}
                onClose={() => this.handlePopoverClose(m._id)}
              >
                <Typography>
                  <a href={m.linkUrl} target="_blank" rel="noopener noreferrer">
                    {m.hoverText}
                  </a>
                </Typography>
              </Popover>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

额外注意点

  • 给<a>标签加上rel="noopener noreferrer",这是使用target="_blank"时的安全最佳实践。
  • 给Popover添加onClose事件,确保点击Popover外部时也能正确关闭对应Popover。

内容的提问来源于stack exchange,提问作者Zanshin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:35:06