ZeeCoder / use-resize-observer

A React hook that allows you to use a ResizeObserver to measure an element's size.
MIT License
644 stars 42 forks source link

Added host to hooks for instanceof check #100

Open nerocui opened 1 year ago

nerocui commented 1 year ago

Hi @ZeeCoder , thanks for creating this library. We are using this package, and we'd like to help make it better. Our use case includes passing in child window's window object. instanceof checks the prototype, and that's unique on each window. So to make sure instanceof works reliably, we need to pass in the host object and compare to the correct Element. Default case is the global window object.

Hope you could give this PR a look when you are free. Thank you!

ZeeCoder commented 1 year ago

Hi @nerocui :wave:

First of all: thank you, I really appreciate the effort here!

Can you provide a reproduction of your issue in codesandbox, so that I better understand your use-case please? :pray:

nerocui commented 1 year ago

@ZeeCoder Happy new year. Hope you had a good holiday break. I was not able to get it to work with codesandbox, but I uploaded a minimal repro to here https://github.com/nerocui/host-repro It's just a express app that host two static html pages. Essentially it's just showing that instanceof returns false if an element is created from a different window. So in order to get instanceof to work reliably, it needs to be called with the type from the host window.

Please let me know if this helps.

ZeeCoder commented 1 year ago

Sorry I didn't come back to you yet, I'm extremely busy with personal stuff right now but I just wanted you to know I appreciate the time you've invested here. Seems like the issue stems from useResolvedElement's use of instanceof here: https://github.com/ZeeCoder/use-resize-observer/blob/490429fd909fe7ff6e65fbf30874b43cd76c24bc/src/utils/useResolvedElement.ts#L30-L37

I think one solution would be to change this:

    const element: T | null = cbElement
      ? cbElement
      : refOrElement
      ? refOrElement instanceof Element
        ? refOrElement
        : refOrElement.current
      : null;

To this:

    const element: T | null = cbElement
      ? cbElement
      : refOrElement
      ? "current" in refOrElement
        ? refOrElement.current
        : refOrElement
      : null;

:point_up: Basically by not using instanceof at all, we circumvent the issue altogether.

Could be a minor release. :thinking:

ZeeCoder commented 1 year ago

Would need an additional test case ofc to ensure there'd be no regressions. Which is where most of the work lies tbh.

nerocui commented 1 year ago

@ZeeCoder Thank you so much for looking into it! Yes getting rid of interaction with global objects would solve this issue.

nerocui commented 1 year ago

I'll update the PR with the recommendation and test cases.

bpinto commented 1 year ago

Alternatively, using hasOwnProperty:

    const element =
      cbElement ||
      (refOrElement
        ? Object.prototype.hasOwnProperty.call(refOrElement, 'current')
          ? refOrElement.current
          : refOrElement
        : null)
ZeeCoder commented 1 year ago

@bpinto looks about right, it's a shame that TS doesn't handle it properly though: image I mean there must be a reason why this isn't working. :thinking:

bpinto commented 1 year ago

Sorry @ZeeCoder, I have 0 experience with TS but a google search showed me the following solution:

Type assertion since we know it's a ref and not an element/state:

const element =
  cbElement ||
  (refOrElement
    ? Object.prototype.hasOwnProperty.call(refOrElement, 'current')
      ? (refOrElement as RefObject<T>).current
      : refOrElement as T
    : null)

I tested it here