Skip to content

Instantly share code, notes, and snippets.

@doubleedesign
Last active March 31, 2023 02:20
Embed
What would you like to do?
useResize React hook. Dynamically get height and width of an element when its size changes and store the values in state. Demo: https://codesandbox.io/s/useresize-demo-de04o8?file=/src/hooks/useResize.ts
import { MutableRefObject, useMemo, useEffect, useState } from 'react';
interface Dimensions {
width: number;
height: number;
}
export function useResize(ref: MutableRefObject<HTMLElement | undefined>, deps: unknown[]): Dimensions {
const [width, setWidth] = useState<number>(0);
const [height, setHeight] = useState<number>(0);
const observer = useMemo(() => {
return new ResizeObserver((entries) => {
// Using the scrollWidth and scrollHeight of the target ensures this works with CSS transitions
// because it accounts for the height of the content before it's visually fully expanded, which elements[0].contentRect does not
setWidth(entries[0].target.scrollWidth);
setHeight(entries[0].target.scrollHeight);
});
}, []);
useEffect(() => {
if (ref.current) {
observer.observe(ref.current);
}
}, [deps, observer, ref]);
return { width, height };
}
import { render, renderHook } from '@testing-library/react';
import React, { MutableRefObject } from 'react';
import { useResize } from './useResize';
function TestElement() {
return (<div data-testid="TestElement">Test content</div>);
}
describe('useResize', () => {
const { container } = render(<TestElement />);
const element: HTMLElement | undefined = container.querySelector('[data-testid="TestElement"]') as HTMLElement || undefined;
const testRef: MutableRefObject<HTMLElement | undefined> = {
current: element
};
const mockResult = { width: 0, height: 0 };
const mockObserve = jest.fn();
beforeEach(() => {
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: mockObserve.mockImplementation((element) => {
mockResult.width = element.scrollWidth;
mockResult.height = element.scrollHeight;
})
}));
});
afterEach(() => {
jest.resetAllMocks();
});
it('calls the observer', () => {
renderHook(() => useResize(testRef, []));
expect(mockObserve).toHaveBeenCalledWith(element);
});
it('gets width and height', () => {
jest.spyOn(element, 'scrollHeight', 'get').mockReturnValue(30);
jest.spyOn(element, 'scrollWidth', 'get').mockReturnValue(30);
renderHook(() => useResize(testRef, []));
expect(mockResult).toEqual({ width: 30, height: 30 });
});
it('detects size change', () => {
jest.spyOn(element, 'scrollHeight', 'get').mockReturnValue(30);
jest.spyOn(element, 'scrollWidth', 'get').mockReturnValue(30);
const { rerender } = renderHook(() => useResize(testRef, []));
expect(mockResult).toEqual({ width: 30, height: 30 });
jest.spyOn(element, 'scrollHeight', 'get').mockReturnValue(50);
jest.spyOn(element, 'scrollWidth', 'get').mockReturnValue(50);
rerender();
expect(mockResult).toEqual({ width: 50, height: 50 });
expect(mockObserve).toHaveBeenCalledTimes(2);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment