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;
}
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>
);
}
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)