use-rect.ts 1.4 KB

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