在使用 useRef 去获取元素时,能拿到一开始的 dom 宽高,但是当后续宽高变化后不会主动更新,所以找到了
ResizeObserver
API, 能监听到变化并通知对应回调
相关知识链接:
Example
自定义 hooks
import React from "react";
export default function useNodeBoundingRect(): [
DOMRectReadOnly | null,
Function,
() => void
] {
const [rect, setRect] = React.useState<DOMRectReadOnly | null>(null);
const resizeObserver = new ResizeObserver((entries) => {
setRect(entries[0].contentRect);
});
const ref = React.useCallback((node) => {
if (node !== null) {
resizeObserver.observe(node);
}
}, []);
const cleanObserver = React.useCallback(() => {
resizeObserver.disconnect();
}, []);
return [rect, ref, cleanObserver];
}
组件部分应用
import * as React from "react";
import styled from "styled-components";
import { observer } from "mobx-react-lite";
import useNodeBoundingRect from "@/common/hooks/useNodeBoundingRect";
const DEFAUTL_EDIT_TOP = 5;
const DEFAULT_TOP_ACTIONS_HEIGHT = 40;
const Actions = observer(() => {
const root = useRootStore() as RootModel;
const { isFormEditing } = root.uiStore;
const { i18n } = Application.getApp();
const [rect, topActions, cleanObserver] = useNodeBoundingRect();
// const topActions = React.useRef(null);
const [topActionsHeight, setTopActionsHeight] = React.useState<number>(
DEFAULT_TOP_ACTIONS_HEIGHT
);
const [editTop, setEditTop] = React.useState<number>(DEFAUTL_EDIT_TOP);
const renderActions = () => {
if (isFormEditing) {
return (
<React.Fragment>
<Button
key="save"
size="s"
type="primary"
onClick={onSave}
loading={isSaving}
>
{i18n.t("Tr-0gnpzf")}
</Button>
<Button key="cancel" size="s" onClick={onCancel}>
{i18n.t("Tr-7ca92e")}
</Button>
</React.Fragment>
);
}
if ("具有编辑权限") {
return (
<Button key="edit" size="s" type="primary" onClick={onEdit}>
{i18n.t("Tr-gqwqhu")}
</Button>
);
}
};
// React.useLayoutEffect(() => {
// const dom = document.getElementById("top-actions");
// // 插件加载完毕
// if (!isPluginLoading && dom && topActions && topActions.current) {
// console.log(topActions.current.clientHeight);
// // 一开始进入页面延时获取顶部插件高度
// setTimeout(() => {
// console.log(dom.clientHeight);
// setTopActionsHeight(dom.clientHeight);
// }, 1500);
// }
// }, [isPluginLoading, topActions]);
React.useEffect(() => {
if (rect && rect.height) {
// 实际上的高度为:react.height + padding + border-width
setTopActionsHeight(rect.height);
}
// cleanup
return () => {
cleanObserver();
};
}, [rect]);
React.useEffect(() => {
setEditTop(isFormEditing ? DEFAUTL_EDIT_TOP : topActionsHeight);
}, [isFormEditing, topActionsHeight]);
return (
<React.Fragment>
<StyledPluginsDiv id="top-actions" ref={topActions}>
{!isFormEditing && <ActionPluginTree />}
</StyledPluginsDiv>
<StyledEditingDiv editTop={editTop}>{renderActions()}</StyledEditingDiv>
</React.Fragment>
);
});
export default Actions;
const StyledEditingDiv = styled.div<{ editTop: number }>`
position: sticky;
top: ${(props) => (props.editTop ? props.editTop + 65 : 105)}px;
right: 0px;
display: flex;
justify-content: flex-end;
`;
const StyledPluginsDiv = styled.div`
position: sticky;
top: 0px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
z-index: 1;
padding: 24px 0;
margin: 0 0 16px -6px;
background: #fff;
border-bottom: 1px solid #eff0f2;
`;