DEV Community

Anxiny
Anxiny

Posted on

Dynamic Dimension React Container with Transition Effect - Part 2, Resize Observer

In part one, we create a dynamic dimension container that can handle child elements with static dimensions.

In this part, we are going to create a dynamic dimension container that can handle child elements that have dynamic dimension.

In order to do that, we need use the Resize Observer API

What is Resize Observer API?

In short term, it's like the onresize event that trigger when, instead of the browser viewport, a document dimension have changed.

For more detail, please refer to MDN

The Code

First, let's create a hook with Resize Observer.

export function useResizeObserver(ref) {
  const [element, setElement] = useState(null);
  const [rect, setRect] = useState({});
  const observer = useRef(null);


  //Clean up observer
  const cleanOb = () => {
    if (observer.current) {
      observer.current.disconnect();
    }
  };

  useEffect(() => {
    setElement(ref.current);
  }, [ref]);

  useEffect(() => {
    if (!element) return;
     // Element has changed, disconnect old observer
    cleanOb();

    const ob = (observer.current = new ResizeObserver(([entry]) => {
      // inlineSize and blockSize in entry.borderBoxSize and contentBoxSize
      // inlineSize means height when write-mode is horizontal, and width when write-mode is vertical.
      // blockSize means width when write-mode is horizontal, and height when write-mode is vertical.
      // So, for the sake of simplicity, I will use getBoundingClientRect
      setRect(entry.target.getBoundingClientRect());
    }));
    ob.observe(element);

    // disconnect when component is unmounted
    return () => {
      cleanOb();
    };
  }, [element]);

  return rect;
}

Enter fullscreen mode Exit fullscreen mode

Note: You can use inlineSize and blockSize in entry.borderBoxSize and entry.contentBoxSize. However, they represent height or width depend on write-mode of target element.

And entry.contentRect may be deprecated in future versions.

Therefore, for the sake of simplicity, I will use getBoundingClientRect here.

Now, let's use this hook in the container we created in part1

function DynamicContainer({ children, className, style }) {
  const content = useRef(null);
  const rect = useResizeObserver(content);

  return (
    <div
      className={className}
      style={{
        transition: "0.3s",
        height: `${rect.height}px`,
        width: `${rect.width}px`,
        overflow: "hidden",
        ...style
      }}
    >
      <div
        ref={content}
        style={{
          width: "fit-content",
          height: "fit-content"
        }}
      >
        {children}
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

And here is a demo, try hover the mouse over the dropdown items.
The dropdown item will change it's text to random length string when hovered.

Thank you all!

Top comments (0)