Skip to content

Commit

Permalink
fix: useResizeObserver now respects element changes within ref obje…
Browse files Browse the repository at this point in the history
…ct (#759)

fix #755
  • Loading branch information
xobotyi committed May 12, 2022
1 parent f70bb5e commit 2a4e848
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 9 deletions.
8 changes: 4 additions & 4 deletions src/useMeasure/useMeasure.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RefObject, useRef } from 'react';
import { useSafeState, IUseResizeObserverCallback, useResizeObserver, useRafCallback } from '..';
import { MutableRefObject, useRef } from 'react';
import { IUseResizeObserverCallback, useRafCallback, useResizeObserver, useSafeState } from '..';

/**
* Uses ResizeObserver to track element dimensions and re-render component when they change.
Expand All @@ -8,8 +8,8 @@ import { useSafeState, IUseResizeObserverCallback, useResizeObserver, useRafCall
*/
export function useMeasure<T extends Element>(
enabled = true
): [DOMRectReadOnly | undefined, RefObject<T>] {
const elementRef = useRef<T>(null);
): [DOMRectReadOnly | undefined, MutableRefObject<T | null>] {
const elementRef = useRef<T | null>(null);
const [rect, setRect] = useSafeState<DOMRectReadOnly>();
const [observerHandler] = useRafCallback<IUseResizeObserverCallback>((entry) =>
setRect(entry.contentRect)
Expand Down
33 changes: 33 additions & 0 deletions src/useResizeObserver/__tests__/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,39 @@ describe('useResizeObserver', () => {
expect(ResizeObserverSpy).toHaveBeenCalledTimes(1);
});

it('should subscribe in case ref first was empty but then gained element', () => {
const div = document.createElement('div');
const ref: React.MutableRefObject<Element | null> = { current: null };
const spy = jest.fn();

// eslint-disable-next-line @typescript-eslint/no-shadow
const { rerender } = renderHook(({ ref }) => useResizeObserver(ref, spy), {
initialProps: { ref },
});

expect(observeSpy).toHaveBeenCalledTimes(0);

ref.current = div;
rerender({ ref });

expect(observeSpy).toHaveBeenCalledTimes(1);

const entry = {
target: div,
contentRect: {},
borderBoxSize: {},
contentBoxSize: {},
} as unknown as ResizeObserverEntry;

ResizeObserverSpy.mock.calls[0][0]([entry]);

expect(spy).not.toHaveBeenCalledWith(entry);

jest.advanceTimersByTime(1);

expect(spy).toHaveBeenCalledWith(entry);
});

it('should invoke each callback listening same element asynchronously using setTimeout0', () => {
const div = document.createElement('div');
const spy1 = jest.fn();
Expand Down
15 changes: 10 additions & 5 deletions src/useResizeObserver/useResizeObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,22 @@ export function useResizeObserver<T extends Element>(
const ro = enabled && getResizeObserver();
const cb = useSyncedRef(callback);

const tgt = target && 'current' in target ? target.current : target;

useEffect(() => {
if (!ro) return;
// this secondary target resolve required for case when we receive ref object, which, most
// likely, contains null during render stage, but already populated with element during
// effect stage.
// eslint-disable-next-line @typescript-eslint/no-shadow
const tgt = target && 'current' in target ? target.current : target;

if (!ro || !tgt) return;

// as unsubscription in internals of our ResizeObserver abstraction can
// happen a bit later than effect cleanup invocation - we need a marker,
// that this handler should not be invoked anymore
let subscribed = true;

const tgt = target && 'current' in target ? target.current : target;
if (!tgt) return;

const handler: IUseResizeObserverCallback = (...args) => {
// it is reinsurance for the highly asynchronous invocations, almost
// impossible to achieve in tests, thus excluding from LOC
Expand All @@ -105,5 +110,5 @@ export function useResizeObserver<T extends Element>(
ro.unsubscribe(tgt, handler);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [target, ro]);
}, [tgt, ro]);
}

0 comments on commit 2a4e848

Please sign in to comment.