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

Question regarding environments missing ResizeObserver #97

Open
martinstark opened this issue Nov 9, 2022 · 6 comments
Open

Question regarding environments missing ResizeObserver #97

martinstark opened this issue Nov 9, 2022 · 6 comments

Comments

@martinstark
Copy link

martinstark commented Nov 9, 2022

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.

@ZeeCoder
Copy link
Owner

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.

@martinstark
Copy link
Author

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 👍

@ZeeCoder ZeeCoder reopened this Nov 11, 2022
@ZeeCoder
Copy link
Owner

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.

@martinstark
Copy link
Author

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.

@ZeeCoder
Copy link
Owner

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. 👍

@martinstark
Copy link
Author

martinstark commented Nov 11, 2022

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: () => {
      // ...
    },
  })

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

No branches or pull requests

2 participants