React hooks to use ResizeObservers.
import { useState } from "react";
import { useObserveResize } from "use-observe-resize";
const Example = () => {
const [width, setWidth] = useState(null);
const observeResize = useObserveResize((entry) => {
setWidth(entry.contentRect.width);
});
return <div ref={observeResize}>{width}</div>;
};
Observe an element for resizes.
function useObserveResize(
onResize: ObserveResizeCallback,
options?: ResizeObserverOptions
): RefCallback<Element>;
type ObserveResizeCallback = (
entry: ResizeObserverEntry,
resizeObserver: ResizeObserver
) => void;
onResize
: The function called when the element resizes.
This function does not need to be stable or memoized (don't call useCallback).
The function takes two parameters:
entry
: A ResizeObserverEntry with size data for the element that resized.resizeObserver
: The ResizeObserver that is observing the element that resized.
options
: The optional options parameter for ResizeObserver.observe().
Returns a callback ref.
The element passed to the callback will be observed.
This function changes if the options
object changes.
Creates a ResizeObserver.
function useResizeObserver(
onResize: ResizeObserverCallback
): [() => ResizeObserver, MutableRefObject<ResizeObserver | undefined>];
onResize
: The function called when observed elements resize.
Same type as the callback
parameter for the ResizeObserver constructor.
Returns an array with two items:
- A stable lazy getter function that will create a ResizeObserver if one hasn't been created yet. This function is stable for all renders.
- A reference object with
.current
set to a ResizeObserver orundefined
.
From the Deep Dive: How to manage a list of refs using a ref callback from the beta react docs:
import { useResizeObserver } from "use-observe-resize";
const Example = (props) => {
const refItemMap = useRef(null);
if (refItemMap.current === null) {
refItemMap.current = new Map();
}
const [getResizeObserver] = useResizeObserver((entries) => {
for (let entry of entries) {
// do w/e
}
});
return (
<ul>
{props.items.map((item) => (
<li
key={item.id}
ref={(element) => {
const resizeObserver = getResizeObserver();
if (element) {
refItemMap.current.set(item.id, element);
resizeObserver.observe(element);
} else {
element = refItemMap.current.get(item.id);
refItemMap.current.delete(item.id);
resizeObserver.unobserve(element);
}
}}
>
{item.text}
</li>
))}
</ul>
);
};
These errors are often benign, but they can be annoying if you have window.onerror
handlers.
Before you ignore these errors, you should try to implement your layout to be top-down.
This would avoid the loop limit errors because nodes that are at or above a previously processed depth wouldn't have resize events.
If your layout can't be top-down or requires 2 passes to stabilize, you can either filter these errors out or try the following pattern to avoid them:
import { useObserveResize } from "use-observe-resize";
const Example = () => {
const observeResize = useObserveResize((entry, resizeObserver) => {
// use entry to make state updates, etc.
// ...
// unobserve this element until the next animation frame
resizeObserver.unobserve(entry.target);
requestAnimationFrame(() => {
// it should be okay to re-observe at this point to avoid loop limit errors
resizeObserver.observe(entry.target);
});
});
return <div ref={observeResize} />;
};
If you need a ref to the observe target, you can create another callback ref that stores the element and sets up the ResizeObserver.
import { useRef, useCallback } from "react";
import { useObserveResize } from "use-observe-resize";
const Example = () => {
const ref = useRef(null);
const observeResize = useObserveResize((entry) => {
// ...
});
const setRef = useCallback(
(element) => {
observeResize(element);
ref.current = element;
},
[observeResize]
);
return <div ref={setRef} />;
};