Skip to content

Instantly share code, notes, and snippets.

@morajabi
Created February 18, 2019 14:35
Show Gist options
  • Save morajabi/523d7a642d8c0a2f71fcfa0d8b3d2846 to your computer and use it in GitHub Desktop.
Save morajabi/523d7a642d8c0a2f71fcfa0d8b3d2846 to your computer and use it in GitHub Desktop.
useRect — getBoundingClientRect() React Hook with resize handler
import { useLayoutEffect, useCallback, useState } from 'react'
export const useRect = (ref) => {
const [rect, setRect] = useState(getRect(ref ? ref.current : null))
const handleResize = useCallback(() => {
if (!ref.current) {
return
}
// Update client rect
setRect(getRect(ref.current))
}, [ref])
useLayoutEffect(() => {
const element = ref.current
if (!element) {
return
}
handleResize()
if (typeof ResizeObserver === 'function') {
let resizeObserver = new ResizeObserver(() => handleResize())
resizeObserver.observe(element)
return () => {
if (!resizeObserver) {
return
}
resizeObserver.disconnect()
resizeObserver = null
}
} else {
// Browser support, remove freely
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}
}, [ref.current])
return rect
}
function getRect(element) {
if (!element) {
return {
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0,
}
}
return element.getBoundingClientRect()
}
@DIEGOHORVATTI
Copy link

DIEGOHORVATTI commented Sep 11, 2023

What I've been using since then to fetch the screen size and the choice of 'resize' or 'scroll' is by prop to avoid having two events with one not being used.

import { useState, useRef, useEffect } from 'react'

type MutableRefObject<T> = {
  current: T
}

type EventType = 'resize' | 'scroll'

const useEffectInEvent = (
  event: EventType,
  useCapture?: boolean,
  set?: () => void
) => {
  useEffect(() => {
    if (set) {
      set()
      window.addEventListener(event, set, useCapture)

      return () => window.removeEventListener(event, set, useCapture)
    }
  }, [])
}

export const useRect = <T extends HTMLDivElement | null>(
  event: EventType = 'resize'
): [DOMRect | undefined, MutableRefObject<T | null>, number] => {
  const [rect, setRect] = useState<DOMRect>()

  const reference = useRef<T>(null)

  const [screenHeight, setScreenHeight] = useState(window.innerHeight)

  const set = (): void => {
    setRect(reference.current?.getBoundingClientRect())
  }

  useEffectInEvent(event, true, set)
  const handleResize = () => {
    setScreenHeight(window.innerHeight)
  }

  useEffect(() => {
    window.addEventListener(event, handleResize)
    return () => {
      window.removeEventListener(event, handleResize)
    }
  }, [])

  return [rect, reference, screenHeight]
}
   const [rect, reference, screenHeight] = useRect('resize')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment