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 'resize observer' #36

Open
KutnerUri opened this issue Aug 10, 2020 · 3 comments
Open

use 'resize observer' #36

KutnerUri opened this issue Aug 10, 2020 · 3 comments

Comments

@KutnerUri
Copy link

In the past I've used resize observer to detect size changes. I think it can also be used in this hook.
Results can also be debounces to ensure good performance.

You can also use this polyfill for compatibility with older browsers.

@samu
Copy link

samu commented Aug 19, 2020

Here's a hook implemented with ResizeObserver.

export function useDimensions<T extends HTMLElement>(): [
  MutableRefObject<any>,
  DOMRectReadOnly | null
] {
  const ref = useRef<T>(null);
  const [dimensions, setDimensions] = useState<DOMRectReadOnly | null>(null);

  const resizeObserver = new ResizeObserver(([entry]) =>
    setDimensions(entry.contentRect)
  );

  useEffect(() => {
    const current = ref.current; // enclose current so the reference can be used in the unsubscribe
    if (current) {
      resizeObserver.observe(current);
      return () => resizeObserver.unobserve(current);
    }

    return undefined;
  }, [ref.current]);

  return [ref, dimensions];
}

@KutnerUri
Copy link
Author

hooks should not declare useRef, they should receive ref from outside.
otherwise you would not be able to use two hooks with refs. :)
see #13

@yuri-scarbaci-lenio
Copy link

yuri-scarbaci-lenio commented Nov 10, 2022

My 2 cents implementation version of this:

const useDimensions = (domNode) => {
  const [dimensions, setDimensions] = React.useState();

  React.useEffect(() => {
    const resizeObserver = new ResizeObserver(([entry]) =>
      setDimensions(entry.contentRect)
    );
    if (domNode) {
      resizeObserver.observe(domNode);
      return () => resizeObserver.unobserve(domNode);
    }

    return undefined;
  }, [domNode]);

  return dimensions;
};

usable as

const Component = ()=>{
  //  ...
  const [parentNode, setParentNode] = React.useState();
  const { width = 960, height = 500 } = useDimensions(parentNode) ?? {};
  // ...
  return (
    <div ref={setParentNode}>children content</div>
  )
}

related explanation on what and why:

const [parentNode, setParentNode] = React.useState(); instead of React.useRef() why?

well, useRef creates a mutable variable, which means it will not trigger useEffect when the component re-renders, useState lets you "react" to dom-node changing and ensure you are always synced up, this has to be used sparingly since it means you will be updating a state after every render which in turns could lead to infinite loops, but when used knowing what you are doing it's handling it's job as expected

ResizeObserver why?

not using the ResizeObserver on elements with dynamic widths/height that are based on viewport sizes ( e.g. 100vh or 100vw) may result in out-of-sync information on extremely memoized code-bases ( not going to provide details on this, but I suggest reading https://attardi.org/why-we-memo-all-the-things/ ) when the user resize the browser window
ResizeObserver will ensure syncing up with such styling measures.
You may want to polyfill resize observer tho if you care about IE https://caniuse.com/resizeobserver
also, additional care must be taken if you need this to work with server side rendering, but that's expected since we are talking about dynamic DOM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants