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