use-resize-observer
use-resize-observer copied to clipboard
Support React 19
NextJS 15 RC just released which requires react 19.
- loosen peer dependency versions to allow it
- check that the library still works as expected with React 19
thanks!
Can't promise you anything, but with a little luck I might get to it this weekend. We'll see.
On Fri, 24 May 2024 at 06:22, Omar Diab @.***> wrote:
NextJS 15 RC just released which requires react 19.
- loosen peer dependency versions to allow it
- check that the library still works as expected with React 19
thanks!
— Reply to this email directly, view it on GitHub https://github.com/ZeeCoder/use-resize-observer/issues/108, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4CKESWA2S6UTH5P5KBPN3ZD26AHAVCNFSM6AAAAABIG2PYG6VHI2DSMVQWIX3LMV43ASLTON2WKOZSGMYTIMZRGE4DSMI . You are receiving this because you are subscribed to this thread.Message ID: @.***>
I'll wait for a stable release actually.
On Fri, 24 May 2024 at 08:31, Viktor Hubert @.***> wrote:
Can't promise you anything, but with a little luck I might get to it this weekend. We'll see.
On Fri, 24 May 2024 at 06:22, Omar Diab @.***> wrote:
NextJS 15 RC just released which requires react 19.
- loosen peer dependency versions to allow it
- check that the library still works as expected with React 19
thanks!
— Reply to this email directly, view it on GitHub https://github.com/ZeeCoder/use-resize-observer/issues/108, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4CKESWA2S6UTH5P5KBPN3ZD26AHAVCNFSM6AAAAABIG2PYG6VHI2DSMVQWIX3LMV43ASLTON2WKOZSGMYTIMZRGE4DSMI . You are receiving this because you are subscribed to this thread.Message ID: @.***>
Next 15 just got released as stable and ready for production, and it depends on React 19 which is still release candidate; upon upgrading I found that the typings for the ref prop are failing because of some upstream changes in React typing; this library wants a RefObject<Element> rather than RefObject<Element | null> so if you call useRef() and give it null as a default before the ref is initialized, that ref cannot be passed.
Haven't tested if anything else fails to work.
Yeah seems like an easy enough fix.
The majority of the work might just be around fixing the type and adding React 19 tests.
Otherwise the code might just work already as-is.
On Tue, Oct 29, 2024, 07:35 Omar Diab @.***> wrote:
Next 15 just got released as stable and ready for production https://nextjs.org/blog/next-15, and it depends on React 19 which is still release candidate; upon upgrading I found that the typings for the ref prop are failing because of some upstream changes in React typing; this library wants a RefObject<Element> rather than RefObject<Element | null> so if you call useRef() and give it null as a default before the ref is initialized, that ref cannot be passed.
Haven't tested if anything else fails to work.
— Reply to this email directly, view it on GitHub https://github.com/ZeeCoder/use-resize-observer/issues/108#issuecomment-2443327691, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4CKEU3PRAZVVR2WICX3ITZ54UCBAVCNFSM6AAAAABIG2PYG6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINBTGMZDONRZGE . You are receiving this because you commented.Message ID: @.***>
Fantastic!
Unfortunately I don't have enough time to make a proper release right now.
The test suite needs upgrading so that it could run both 18 and 19, types changed significantly as well, and my gut feel is that the tests will yield some issues.
@ZeeCoder please do keep us updated with any ETA as to when we can expect react 19 support here.
I'll probably post in this PR as I go: https://github.com/ZeeCoder/use-resize-observer/pull/114
Is there hope for an update in the near future?
A native implementation comes out of the box:
import React, {
useState,
useCallback,
useEffect
} from 'react';
const useSize = <T extends React.RefObject<HTMLDivElement | null>>(target: T) => {
const [size, setSize] = useState<DOMRect>();
const updateSize = useCallback(() => {
if (target.current) {
const size = target.current.getBoundingClientRect();
setSize(size);
}
}, [target]);
useEffect(() => {
const {current} = target;
updateSize();
const observer = new ResizeObserver((entries) => {
if (entries.length > 0) {
updateSize();
}
});
if (current) {
observer.observe(current);
}
return () => {
if (current) {
observer.unobserve(current);
}
observer.disconnect();
};
}, [target, updateSize]);
return size;
};
export {useSize};
const target = useRef<HTMLDivElement>(null);
const size = useSize(target); // size.width
A native implementation comes out of the box:
import React, { useState, useCallback, useEffect } from 'react';
const useSize = <T extends React.RefObject<HTMLDivElement | null>>(target: T) => { const [size, setSize] = useState<DOMRect>();
const updateSize = useCallback(() => { if (target.current) { const size = target.current.getBoundingClientRect(); setSize(size); } }, [target]); useEffect(() => { const {current} = target; updateSize(); const observer = new ResizeObserver((entries) => { if (entries.length > 0) { updateSize(); } }); if (current) { observer.observe(current); } return () => { if (current) { observer.unobserve(current); } observer.disconnect(); }; }, [target, updateSize]); return size;};
export {useSize};
const target = useRef<HTMLDivElement>(null); const size = useSize(target); // size.width
Thanks. Seems to work good (y)
A native implementation comes out of the box:
import React, { useState, useCallback, useEffect } from 'react';
const useSize = <T extends React.RefObject<HTMLDivElement | null>>(target: T) => { const [size, setSize] = useState<DOMRect>();
const updateSize = useCallback(() => { if (target.current) { const size = target.current.getBoundingClientRect(); setSize(size); } }, [target]); useEffect(() => { const {current} = target; updateSize(); const observer = new ResizeObserver((entries) => { if (entries.length > 0) { updateSize(); } }); if (current) { observer.observe(current); } return () => { if (current) { observer.unobserve(current); } observer.disconnect(); }; }, [target, updateSize]); return size;};
export {useSize};
const target = useRef<HTMLDivElement>(null); const size = useSize(target); // size.width
this will break if the ref gets updated. using refs as hook dependency is brittle.
A native implementation comes out of the box: import React, { useState, useCallback, useEffect } from 'react'; const useSize = <T extends React.RefObject<HTMLDivElement | null>>(target: T) => { const [size, setSize] = useState();
const updateSize = useCallback(() => { if (target.current) { const size = target.current.getBoundingClientRect(); setSize(size); } }, [target]); useEffect(() => { const {current} = target; updateSize(); const observer = new ResizeObserver((entries) => { if (entries.length > 0) { updateSize(); } }); if (current) { observer.observe(current); } return () => { if (current) { observer.unobserve(current); } observer.disconnect(); }; }, [target, updateSize]); return size;}; export {useSize}; const target = useRef(null); const size = useSize(target); // size.width
this will break if the ref gets updated. using refs as hook dependency is brittle.
useRef returns a stable object, and useEffect does not react to changes in its properties. In most scenarios, this code is reliable, and you can safely omit the ref from the dependency array if needed.
exactly. if target.current changes the effect will not run and the wrong element will be observed
Here you go — should work, I think.
import React, {
useState,
useRef,
useCallback,
useEffect
} from 'react';
const useRefSize = () => {
const [size, setSize] = useState<DOMRect>();
const observerRef = useRef<ResizeObserver | null>(null);
const refCallback = useCallback((node: HTMLElement | null) => {
if (observerRef.current) {
observerRef.current.disconnect();
observerRef.current = null;
}
if (node) {
const updateSize = () => {
const rect = node.getBoundingClientRect();
setSize(rect);
};
updateSize();
const observer = new ResizeObserver(() => {
updateSize();
});
observer.observe(node);
observerRef.current = observer;
}
}, []);
return [refCallback, size] as const;
};
nice. that's much better pattern.
is there a reason why you recreate the resizeobserver on each ref callback call? just disconnect / observe should be enough. A further optimization would even be to reuse the same observer instance for all "instances" of this hook. https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ
The initial size may be determined in a layout effect to prevent unecessary rerenders, size state should not be (re-) set if no change. Having a callback passed to the hook, would be nice for being able to have custom logic on resize.
For some application it is critical to have minimal performance overhead. Resize is a high frequency event.