Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ResizeObserver for useElementSize #236

Closed
marnusw opened this issue Oct 14, 2022 · 3 comments
Closed

Use ResizeObserver for useElementSize #236

marnusw opened this issue Oct 14, 2022 · 3 comments
Labels
bug Something isn't working

Comments

@marnusw
Copy link
Contributor

marnusw commented Oct 14, 2022

The useElementSize hook doesn't react to changes in the size of the element during CSS animations, instead it jumps to the final size instantaneously. This because useEventListener('resize', handleSize) listens to window resize events, and not actually element resize events.

I'd like to propose using ResizeObserver for this hook instead of the current implementation. ResizeObserver is supported by all modern browsers, only IE used to block it but it's now reached end of life (and it can be polyfilled). If it will be of interest I will work on a PR.

I also think it might be OK to use useRef instead of manually managing the ref instance. If the element the ref points to changes I think that would re-render this hook in most if not all cases. Related issues are #196, #205, #217

import { RefObject, useRef, useState } from 'react'
import { useIsomorphicLayoutEffect } from 'usehooks-ts'

interface Size {
  width: number
  height: number
}

export function useElementSize<T extends HTMLElement = HTMLDivElement>(): [RefObject<T>, Size,] {
  const ref = useRef<T>(null)
  const [size, setSize] = useState<Size>({
    width: 0,
    height: 0,
  })

  useIsomorphicLayoutEffect(() => {
    setSize({
      width: ref.current?.offsetWidth || 0,
      height: ref.current?.offsetHeight || 0,
    })

    const resizeObserver = new ResizeObserver((entries) => {
      const entry = entries[0]
      if (entry) {
        setSize({
          width: entry.contentRect.width || 0,
          height: entry.contentRect.height || 0,
        })
      }
    })

    ref.current && resizeObserver.observe(ref.current)
    return () => {
      ref.current && resizeObserver.unobserve(ref.current)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [ref.current])

  return [ref, size]
}
@codebutler
Copy link

The values returned by ReiszeObserver don't include padding/border. Something like this would better match the existing behavior:

export const useElementSize = <T extends HTMLElement = HTMLDivElement>(): [
  RefObject<T>,
  Size
] => {
  const ref = useRef<T>(null);
  const [size, setSize] = useState<Size>({
    width: 0,
    height: 0,
  });

  useIsomorphicLayoutEffect(() => {
    const updateSize = (element: Element | null) => {
      const { width, height } = element?.getBoundingClientRect() ?? {
        width: 0,
        height: 0,
      };
      setSize({ width, height });
    };

    updateSize(ref.current);

    const resizeObserver = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (entry) {
        updateSize(entry.target);
      }
    });

    ref.current && resizeObserver.observe(ref.current);
    return () => {
      ref.current && resizeObserver.unobserve(ref.current);
    };
  }, [ref.current]);

  return [ref, size];
};

@1kuko3
Copy link

1kuko3 commented Dec 21, 2022

I had a similar issue with useElementSize - the hook did not reflect the size when the element was unmounted/mounted again as it uses the resize event listener which isn't triggered when the component mounts/unmounts compared to ResizeObserver solution. @codebutler's solution works well for my purposes ✌️

@juliencrn juliencrn added the bug Something isn't working label Jan 7, 2024
@juliencrn
Copy link
Owner

Hi, useElementSize has been deprecated and should be replaced by useResizeObserver (indeed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants