use-rect.ts 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. // based on https://gist.github.com/morajabi/523d7a642d8c0a2f71fcfa0d8b3d2846?permalink_comment_id=4688158#gistcomment-4688158
  2. import type { RefObject } from 'react';
  3. import { useCallback, useEffect, useState } from 'react';
  4. type MutableRefObject<T> = {
  5. current: T;
  6. };
  7. type EventType = 'resize' | 'scroll';
  8. const useEffectInEvent = (
  9. event: EventType,
  10. useCapture?: boolean,
  11. set?: () => void,
  12. ) => {
  13. useEffect(() => {
  14. if (set) {
  15. set();
  16. window.addEventListener(event, set, useCapture);
  17. return () => window.removeEventListener(event, set, useCapture);
  18. }
  19. }, [event, set, useCapture]);
  20. };
  21. export const useRect = <T extends HTMLDivElement | null>(
  22. reference: RefObject<T>,
  23. event: EventType = 'resize',
  24. ): [DOMRect | undefined, MutableRefObject<T | null>, number] => {
  25. const [rect, setRect] = useState<DOMRect>();
  26. const [screenHeight, setScreenHeight] = useState(window.innerHeight);
  27. const set = useCallback(() => {
  28. setRect(reference.current?.getBoundingClientRect());
  29. }, [reference]);
  30. useEffectInEvent(event, true, set);
  31. const handleResize = useCallback(() => {
  32. setScreenHeight(window.innerHeight);
  33. }, []);
  34. useEffect(() => {
  35. window.addEventListener(event, handleResize);
  36. return () => {
  37. window.removeEventListener(event, handleResize);
  38. };
  39. }, [event, handleResize]);
  40. return [rect, reference, screenHeight];
  41. };