ZeeCoder / use-resize-observer

A React hook that allows you to use a ResizeObserver to measure an element's size.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question regarding environments missing ResizeObserver

martinstark opened this issue · comments

I have a library where I don't necessarily want to polyfill ResizeObserver if a device is missing support, meaning I'd like to avoid calling useResizeObserver when ResizeObserver does not exist in DOM.

Due to React semantics, I can't make the call to the hook conditional.

Would it be reasonable to extend the hook with an option to disable resize detection when it isn't supported (e.g. by blocking calls to new ResizeObserver internally)?

I know that I can add the hook to a conditionally rendered element in order to prevent it from running if ResizeObserver isn't in DOM, e.g.:

  { ("ResizeObserver" in window) && <SomeComponentWithResizeObserver callback={setSomeStateOnResize} /> }

However, if my library only exports a hook and no component, that wouldn't be possible.

I can create a PR if this sounds reasonable.

I think you might be able to achieve what you want with the current hook.
When you don't pass in an element to to hook to be observed, then the hook doesn't create an RO instance, and therefore won't need a polyfill either up until that point.

This lazy-initialization was introduced for SSR compatibility initially, but could be used for this as follows:

import useResizeObserverRaw = 'use-resize-observer';

const isRoAvailable = typeof window !== 'undefined' && ("ResizeObserver" in window);

export const useResizeObserver = () => {
  const { ref: refRaw, with, height } = useResizeObserverRaw();

  const ref = useCallback((element) => {
    if (isRoAvailable) { refRaw(element); }
  }, [refRaw, isRoAvailable]);

  return useMemo(() => ({ref, width, height}), [ref, width, height])
}

Then you'd use this hook as usual:

const { ref, width = 42, height = 24 } = useResizeObserver();

☝️ Where the default values would be used when no RO is available.

I'm a bit unsure how this would work with SSR, but I think when hydration happens it would call the ref passed in with the new isRoAvailable variable available to it.
Of course you can do other things as well depending on what you need in the if (isRoAvailable) check, not just skipping hook initialisation.

Hope this helps.

Thanks @ZeeCoder, that's great to know. I should have looked more closely at the source. I don't expect this to be a very common usecase, but could be useful to have this behaviour documented 👍

Great, I'll leave this issue open until it's documented, I think it's an interesting use-case.
@martinstark can you just also confirm when you did the implementation if there were anything else that needed to be considered, or if it was just copy-c of the above code?
Just so I'd know what to document.

I'm using the inverted version where I'm passing a ref into the hook, seems to work in an even easier manner:

  delete window.ResizeObserver;

  // ...

  useResizeObserver({
    ref: null,
    onResize: () => {
      // ...
    },
  });

Triggers no error.

Thanks for sharing!
That's not something I can recommend in the docs with the delete and all though, so I'll probably just use my original recommendation.

It seems like the idea behind it is already enough to nudge people in the right direction anyway. 👍

I just used delete in order to quickly test what happens in an environment where ResizeObserver is missing and calling it would throw an error. Setting ref to null prevents calls to ResizeObserver. That delete is not part of my actual code 😬

Real code example would be something like:

  useResizeObserver({
    // prevent crashing in environments that do not support ResizeObserver
    ref: isRoAvailable ? someRef : null,
    onResize: () => {
      // ...
    },
  })