MUI Avatar组件Hover时弹出Card组件出现闪烁问题求助
问题分析与解决
闪烁原因
你遇到的闪烁问题不是key的问题,核心原因有两个:
- Card显示时会占据文档流空间,导致布局移位,鼠标意外离开Avatar触发
onMouseOut;Card消失后布局复原,鼠标又回到Avatar触发onMouseOver,循环往复造成闪烁。 - 鼠标从Avatar移到弹出的Card上时,会触发Avatar的
onMouseOut事件,直接清空hoveredEmployee,导致Card瞬间消失。
解决方案
1. 改用容器级hover事件
把onMouseEnter和onMouseLeave绑定到包裹Avatar和Card的外层div上,这样鼠标移到Card上时,不会触发离开事件(因为Card属于容器内部元素)。
2. 给Card设置绝对定位
让Card脱离文档流,避免显示/隐藏时改变页面布局,彻底解决移位导致的闪烁问题。
修改后的完整代码
import { Avatar, Card, CardContent, Grid, Paper, Typography } from '@mui/material'; import React, { useState, useMemo } from 'react'; import { FunctionComponent, ReactElement } from 'react'; export const People: FunctionComponent = (): ReactElement => { const [hoveredEmployee, setHoveredEmployee] = useState<string | null>(null); // 补全原代码缺失的数据源示例 const employeeDetails = [{ loginName: 'sammy', fullName: 'sammy chung' }]; const employeeDetailsMapped = { sammy: { loginName: 'smy', fullName: 'sammy chung' } }; // 用useMemo优化分组逻辑,避免重复计算 const groupedUsers = useMemo(() => { const groups: Record<string, string[]> = {}; employeeDetails.forEach((item) => { const firstLetter = item.loginName.charAt(0).toUpperCase(); if (!groups[firstLetter]) { groups[firstLetter] = [item.loginName]; } else { groups[firstLetter].push(item.loginName); } }); return groups; }, [employeeDetails]); return ( <> <Grid container spacing={2}> <Paper elevation={2} sx={{ p: 3, my: 2, width: '100%', overflowY: 'auto', maxHeight: 600 }}> {Object.entries(groupedUsers).map(([letter, names]) => ( <Grid item key={letter} sx={{ borderBottom: '1px solid #ddd', pb: 2, mb: 2 }}> <h3>{letter}</h3> {names.map((name: string) => ( <div key={name} style={{ display: 'inline-block', marginRight: 10, position: 'relative', // 作为Card绝对定位的父容器 }} onMouseEnter={() => setHoveredEmployee(name)} onMouseLeave={() => setHoveredEmployee(null)} > <Avatar variant='rounded' alt={`Photo of ${employeeDetailsMapped[name].loginName}`} src={`...`} sx={{ height: 80, width: 80, mr: 2 }} /> {hoveredEmployee === name && ( <Card elevation={3} sx={{ padding: 2, height: 'auto', width: '480px', position: 'absolute', // 脱离文档流,避免布局移位 top: 0, left: '100%', // 显示在Avatar右侧 ml: 2, zIndex: 1000, // 确保Card在最上层 ':hover': { boxShadow: 20, }, }} > <CardContent> <div style={{ display: 'flex', alignItems: 'center' }}> <Avatar variant='rounded' alt={`Badge photo for ${employeeDetailsMapped[name].loginName}`} src={`...`} sx={{ height: 130, width: 130 }} /> <Typography variant='h5' fontWeight={500} marginLeft={3}> {employeeDetailsMapped[name].fullName} </Typography> </div> </CardContent> </Card> )} </div> ))} </Grid> ))} </Paper> </Grid> </> ); };
额外优化说明
- 用
useMemo包裹分组逻辑,避免组件每次渲染都重复计算分组,提升性能。 - 修正了原代码中
employeeDetails未定义的问题,补全了示例数据源。 - 调整了Card内的布局样式,让内容对齐更合理。
内容的提问来源于stack exchange,提问作者PandasM
相关产品推荐
相关产品推荐

