react-use/src/useMeasure.ts
Torleif Berger bedbad7231
fix: 🐛 correct useMeasure typings
When upgrading to v15, I discovered some issues with `useMeasure` forcing me to downgrade to v14 again. Hopefully these changes fixes those issues...

* It was no longer possible to define the ref type, meaning components requiring a certain type of ref would reject it. Fixed this by making the function generic again.
* Previously you _had to_ define the ref type, while in v15 you _couldn't_. Fixed this by giving the generic type a default type of `HTMLElement`.
* The implicit typing of `useMeasureMock` combined with the ternary default export statement made the imported type of `useMeasure` very messy. Fixed this by making the type of `useMeasureMock` explicitly match `useMeasure`.
2020-05-18 14:35:40 +02:00

52 lines
1.4 KiB
TypeScript

import { useState, useMemo } from 'react';
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
import { isClient } from './util';
export type UseMeasureRect = Pick<
DOMRectReadOnly,
'x' | 'y' | 'top' | 'left' | 'right' | 'bottom' | 'height' | 'width'
>;
export type UseMeasureRef<E extends HTMLElement = HTMLElement> = (element: E) => void;
export type UseMeasureResult<E extends HTMLElement = HTMLElement> = [UseMeasureRef<E>, UseMeasureRect];
const defaultState: UseMeasureRect = {
x: 0,
y: 0,
width: 0,
height: 0,
top: 0,
left: 0,
bottom: 0,
right: 0,
};
const useMeasure = <E extends HTMLElement = HTMLElement>(): UseMeasureResult<E> => {
const [element, ref] = useState<E | null>(null);
const [rect, setRect] = useState<UseMeasureRect>(defaultState);
const observer = useMemo(
() =>
new (window as any).ResizeObserver(entries => {
if (entries[0]) {
const { x, y, width, height, top, left, bottom, right } = entries[0].contentRect;
setRect({ x, y, width, height, top, left, bottom, right });
}
}),
[]
);
useIsomorphicLayoutEffect(() => {
if (!element) return;
observer.observe(element);
return () => {
observer.disconnect();
};
}, [element]);
return [ref, rect];
};
const useMeasureMock: typeof useMeasure = () => [() => {}, defaultState];
export default (isClient && !!(window as any).ResizeObserver) ? useMeasure : useMeasureMock;