When useRef is used to obtain elements, the initial DOM width and height can be obtained, but will not be actively updated when the subsequent width and height changes. Therefore, WE found the ResizeObserver API, which can listen for changes and notify corresponding callbacks
Related knowledge links:
- ResizeObserver
- getBoundingClientRect
- react-hooks
Example
Custom 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];
}
Copy the code
Component part application
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 ("With edit permission") {
return (
<Button key="edit" size="s" type="primary" onClick={onEdit}>
{i18n.t("Tr-gqwqhu")}
</Button>); }};// React.useLayoutEffect(() => {
// const dom = document.getElementById("top-actions");
The // // plug-in is loaded
// if (! isPluginLoading && dom && topActions && topActions.current) {
// console.log(topActions.current.clientHeight);
// // Start by entering the page delay to obtain the height of the top plug-in
// 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; `;
Copy the code