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
相关产品推荐
相关产品推荐

