Skip to content

Commit

Permalink
feat(useMeasure,useResizeObserver): ability yo disable observation (#573
Browse files Browse the repository at this point in the history
)

Added extra parameter that allows to enable/disable observation 
dynamically.

#fix: #523
  • Loading branch information
xobotyi authored Jan 11, 2022
1 parent fbe6f8d commit 4c6f074
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 10 deletions.
7 changes: 6 additions & 1 deletion src/useMeasure/__docs__/story.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Uses ResizeObserver to track element dimensions and re-render component when the
- Its ResizeObserver callback uses RAF debouncing, therefore it is pretty performant.
- SSR friendly, returns `undefined` on initial mount.
- Automatically creates ref for you, that you can easily pass to needed element.
- Allows to dynamically enable and disable observation.

#### Example

Expand All @@ -21,13 +22,17 @@ Uses ResizeObserver to track element dimensions and re-render component when the
## Reference

```ts
function useMeasure<T extends Element>(): [DOMRectReadOnly | undefined, RefObject<T>];
function useMeasure<T extends Element>(enabled = true): [DOMRectReadOnly | undefined, RefObject<T>];
```

#### Importing

<ImportPath />

#### Arguments

- **enabled** _`boolean`_ _(default: `true`)_ - Whether resize observer is enabled or not.

#### Return

Array of two elements:
Expand Down
8 changes: 6 additions & 2 deletions src/useMeasure/useMeasure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import { useSafeState, IUseResizeObserverCallback, useResizeObserver, useRafCall

/**
* Uses ResizeObserver to track element dimensions and re-render component when they change.
*
* @param enabled Whether resize observer is enabled or not.
*/
export function useMeasure<T extends Element>(): [DOMRectReadOnly | undefined, RefObject<T>] {
export function useMeasure<T extends Element>(
enabled = true
): [DOMRectReadOnly | undefined, RefObject<T>] {
const elementRef = useRef<T>(null);
const [rect, setRect] = useSafeState<DOMRectReadOnly>();
const [observerHandler] = useRafCallback<IUseResizeObserverCallback>((entry) =>
setRect(entry.contentRect)
);

useResizeObserver(elementRef, observerHandler);
useResizeObserver(elementRef, observerHandler, enabled);

return [rect, elementRef];
}
7 changes: 5 additions & 2 deletions src/useResizeObserver/__docs__/story.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Invokes a callback whenever ResizeObserver detects a change to target's size.
- Does not apply any throttle or debounce mechanism - it is on end-developer side.
- Does not produce references for you.
- SSR friendly.
- Provides access to `ResizeObserverEntry`
- Provides access to `ResizeObserverEntry`.
- Allows to dynamically enable and disable observation.

#### Example

Expand All @@ -33,7 +34,8 @@ Invokes a callback whenever ResizeObserver detects a change to target's size.
```ts
export function useResizeObserver<T extends Element>(
target: RefObject<T> | T | null,
callback: (entry: ResizeObserverEntry) => void
callback: (entry: ResizeObserverEntry) => void,
enabled = true
): void;
```

Expand All @@ -45,3 +47,4 @@ export function useResizeObserver<T extends Element>(

- **target** _`RefObject<Element> | Element | null`_ - element to track.
- **callback** _`(entry: ResizeObserverEntry) => void`_ - Callback that will be invoked on resize.
- **enabled** _`boolean`_ _(default: `true`)_ - Whether resize observer is enabled or not.
36 changes: 36 additions & 0 deletions src/useResizeObserver/__tests__/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,40 @@ describe('useResizeObserver', () => {
expect(unobserveSpy).toHaveBeenCalledTimes(1);
expect(unobserveSpy).toHaveBeenCalledWith(div);
});

describe('disabled observer', () => {
it('should not subscribe in case observer is disabled', () => {
const div = document.createElement('div');
const div2 = document.createElement('div');
const spy1 = jest.fn();
const spy2 = jest.fn();

renderHook(() => useResizeObserver(div, spy1));
renderHook(() => useResizeObserver({ current: div2 }, spy2, false));

expect(observeSpy).toHaveBeenCalledTimes(1);
});

it('should unsubscribe and resubscribe in case of observer toggling', () => {
const div = document.createElement('div');
const spy1 = jest.fn();

const { rerender } = renderHook(({ enabled }) => useResizeObserver(div, spy1, enabled), {
initialProps: { enabled: false },
});

expect(observeSpy).toHaveBeenCalledTimes(0);
expect(unobserveSpy).toHaveBeenCalledTimes(0);

rerender({ enabled: true });

expect(observeSpy).toHaveBeenCalledTimes(1);
expect(unobserveSpy).toHaveBeenCalledTimes(0);

rerender({ enabled: false });

expect(observeSpy).toHaveBeenCalledTimes(1);
expect(unobserveSpy).toHaveBeenCalledTimes(1);
});
});
});
9 changes: 4 additions & 5 deletions src/useResizeObserver/useResizeObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,17 @@ function getResizeObserver(): IResizeObserverSingleton | undefined {
*
* @param target React reference or Element to track.
* @param callback Callback that will be invoked on resize.
* @param enabled Whether resize observer is enabled or not.
*/
export function useResizeObserver<T extends Element>(
target: RefObject<T> | T | null,
callback: IUseResizeObserverCallback
callback: IUseResizeObserverCallback,
enabled = true
): void {
const ro = getResizeObserver();
const ro = enabled && getResizeObserver();
const cb = useSyncedRef(callback);

useEffect(() => {
// quite difficult to cover with tests, but the 'if' branch is pretty
// straightforward: do nothing, it is safe to exclude from LOC
/* istanbul ignore if */
if (!ro) return;

// as unsubscription in internals of our ResizeObserver abstraction can
Expand Down

0 comments on commit 4c6f074

Please sign in to comment.