Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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()
}
@intelliapps-io

This comment has been minimized.

Copy link

intelliapps-io commented Jul 26, 2019

I made some changes so it can be used with TypeScript. https://codesandbox.io/s/userect-hook-1y5t7

@vincerubinetti

This comment has been minimized.

Copy link

vincerubinetti commented Nov 15, 2019

Here's an alternative that I came up with that seems to work:

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

export const useBbox = () => {
  const ref = useRef();
  const [bbox, setBbox] = useState({});

  const set = () =>
    setBbox(ref && ref.current ? ref.current.getBoundingClientRect() : {});

  useEffect(() => {
    set();
    window.addEventListener('resize', set);
    return () => window.removeEventListener('resize', set);
  }, []);

  return [bbox, ref];
};

Then to use it:

const SignIn = () => {
  const [bbox, ref] = useBbox();

  return (
    <>
      <Button ref={ref}>open popup</Button>
      <Popup anchorBbox={bbox}>popup content</Popup>
    </>
  );
};

This is modeled after this example in the React docs, but is modified to also update the bbox any time the window is resized (which may or may not be enough for your use case; use this with caution).

It seems to unmount the listener properly, but there could definitely be something I missed. Comments are welcome.


Related:
facebook/react#15176

Also here's an example of a library using hooks to update positions of a popup, but with added debouncing and other stuff:
https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Popover/Popover.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.