React使用state更新实现hover效果卡顿问题(MUI Tree View场景)
卡顿根因
当前实现通过全局React状态存储当前悬浮的元素ID,每次悬浮触发状态更新时,会导致所有TreeItem组件全量重渲染,当节点数量较多时,重渲染开销过大就会出现卡顿问题。
最优解决方案:使用CSS原生hover实现交互
原生CSS hover完全不需要JS介入,不会触发任何组件重渲染,性能最优,可覆盖你需要的背景色变更、提示文本显示两种需求:
- 调整JSS样式定义:
// 列表项根容器样式 treeItemRoot: { backgroundColor: "transparent", transition: "background-color 0.1s", // 悬浮态背景色 "&:hover": { backgroundColor: "grey", }, // 选中态背景色,优先级高于悬浮态 "&.selected": { backgroundColor: "red", "&:hover": { backgroundColor: "darkred", // 可自定义选中后悬浮的背景色 } } }, // 选择提示文本样式 selectUnSelectText: { display: "none", // 默认隐藏 // 父容器悬浮时显示文本 ".treeItemRoot:hover &": { display: "inline-block", } }
- 简化JS逻辑,移除所有hover相关的状态和事件:
删除原有hoverElementId状态、handleTreeItemMouseHover函数,调整getTreeItemLabel逻辑如下:
const getTreeItemLabel = (labelText, labelData) => { const selectedKey = generateKey(labelData.nodeId, labelText); const isItemSelected = !!selectedRegionsLocalState[selectedKey]; return ( <Grid container justify="space-between" className={`${classes.treeItemRoot} ${isItemSelected ? 'selected' : ''}`} > <Grid item className={classes.treeItemLabelText}>{labelText}</Grid> <Grid item className={classes.selectUnSelectText} onClick={(event) => { handleRegionSelectUnselect( labelData, selectedKey, !isItemSelected ) event.preventDefault(); }} > {isItemSelected ? Constants.ClickToDeselectRegion : Constants.ClickToSelectRegion} </Grid> </Grid> ) }
备选方案:特殊场景需要JS控制hover时用局部DOM操作
如果有业务逻辑必须要JS介入悬浮事件,不要用全局状态存储悬浮ID,直接操作当前触发事件的DOM节点即可,只会修改当前节点不会触发全量重渲染:
const handleMouseEnter = (e) => { e.currentTarget.classList.add(classes.hovered); // 其他需要执行的业务逻辑 } const handleMouseLeave = (e) => { e.currentTarget.classList.remove(classes.hovered); // 其他需要执行的业务逻辑 } // 仅给Grid绑定上述两个事件,不更新任何全局state
额外优化建议
如果节点规模超过100个,可开启MUI TreeView的虚拟滚动能力,只渲染视口范围内的节点,进一步降低渲染开销。
内容的提问来源于stack exchange,提问作者ash1102
相关产品推荐
相关产品推荐

