reactjs - 为什么当底层组件重新渲染时popper会跳到左上角?
问题描述
我正在使用 Material-UI Popper 组件(它又使用 popper.js)来创建一个悬停工具栏。在大多数情况下,它运行良好,除了一种奇怪的行为:
- 选择一些文本:悬停的工具栏出现在文本上方 - 正如预期的那样。
- 选择工具栏中的任何按钮:执行相应的操作。但是,工具栏会跳到窗口的左上角。见下文。
您可以在我的 Storybook中尝试这种行为- 只需选择一些文本并单击其中一个“T”按钮。
基本问题围绕popper的定位:
- 当用户选择一些文本时,会创建一个虚假的虚拟元素并将其作为锚元素传递给 popper。Popper 使用它
anchorEl
来定位悬停工具栏。到现在为止还挺好。 - 当用户单击工具栏中的按钮时,悬停的工具栏会跳到窗口的左上角。
我猜这是因为当底层组件重新渲染时,锚元素会以某种方式丢失。我不知道为什么,但这只是我的理论。有人可以帮我解决这个问题吗?
计算 的代码anchorEl
位于 ReactuseEffect()
中。我已确保 的依赖项列表useEffect
是准确的。我可以看到,当工具栏跳转时,useEffect()
没有被调用,这意味着anchorEl
没有被重新计算。这让我相信工具栏应该在当前位置保持原样,而不是跳转到 (0,0)。但这并没有发生:-(。
这是useEffect()
工具栏组件内的代码。你可以在我的 repo中找到完整的代码。任何帮助将非常感激。
useEffect(() => {
if (editMode === 'toolbar') {
if (isTextSelected) {
const domSelection = window.getSelection();
if (domSelection === null || domSelection.rangeCount === 0) {
return;
}
const domRange = domSelection.getRangeAt(0);
const rect = domRange.getBoundingClientRect();
setAnchorEl({
clientWidth: rect.width,
clientHeight: rect.height,
getBoundingClientRect: () =>
domRange.getBoundingClientRect(),
});
setToolbarOpen(true);
} else {
setToolbarOpen(false);
}
} else {
setToolbarOpen(false);
}
}, [editMode, isTextSelected, selection, selectionStr]);
解决方案
我相信您在toggleBlockdomRange
完成工作后不再有效(由于 dom 节点被替换),因此不再返回任何有意义的东西。getBoundingClientRect
您应该能够通过重做获取 anchorEl 范围内的工作来解决此问题getBoundingClientRect
。可能类似于以下内容(我没有尝试执行它,所以不能保证没有小错误):
const getSelectionRange = () => {
const domSelection = window.getSelection();
if (domSelection === null || domSelection.rangeCount === 0) {
return null;
}
return domSelection.getRangeAt(0);
};
useEffect(() => {
if (editMode === "toolbar") {
if (isTextSelected) {
const domRange = getSelectionRange();
if (domRange === null) {
return;
}
const rect = domRange.getBoundingClientRect();
setAnchorEl({
clientWidth: rect.width,
clientHeight: rect.height,
getBoundingClientRect: () => {
const innerDomRange = getSelectionRange();
return innerDomRange === null
? null
: innerDomRange.getBoundingClientRect();
}
});
setToolbarOpen(true);
} else {
setToolbarOpen(false);
}
} else {
setToolbarOpen(false);
}
}, [editMode, isTextSelected, selection, selectionStr]);
推荐阅读
- wordpress - 如何从 foreach 循环中删除重复的结果
- javascript - 如何将 react-chartjs-2 与 chartjs-plugin-datasource 集成以在 s3 url 处使用数据源
- javascript - 找不到“EJS”打字稿和快递
- django - 如何通过单个链接发送重置密码令牌
- macos - 对编程有点新意 - 将大量与编程相关的文件上传到 iCloud
- bash - 无法在不终止 docker 容器中的 ssh 服务器的情况下终止节点(js)进程
- mysql - 使用 laravel 6 从 Blob 列显示文档
- c++ - 无法进入while循环:“分段错误”
- reactjs - process.env.NODE_ENV === "开发" 即使在生产中
- javascript - 谷歌浏览器在表单自动完成时触发 keydown 事件