Giter VIP home page Giter VIP logo

Comments (24)

ashen-dawn avatar ashen-dawn commented on May 30, 2024 17

I had to do some research on this for work, and found a bit of background information that may be helpful. If I get a bit of time I'll try to make a reproducible code sample (although I can't promise that right now).

Background Info:

I believe this error is the same as the one listed here as part of the ResizeObserver spec. Chromium based browsers seem to use a different error message than that exact one, but in Firefox the error message is the same.

This error is emitted if a resize event happens and the browser is unable to notify all the listeners within that frame. In our app this happens because we update some fairly global React state directly from the react-resize-detector callback, which triggered several React components to re-render at once. Because React re-renders synchronously, this delayed the browser's paint until a frame or two later, causing this error.

I'm not sure if the browser eventually does notify all the listeners and this warning just means there was a dropped frame, or if some of them just get skipped for that event? If it's the former then this is potentially an error that is safe to ignore, if it's the latter than probably not.

Why this error is hard to replicate:

Because ResizeObserver emits its errors as events on the window object, these normally don't get logged. The tricky thing is that because monitoring platforms like Sentry often use window.addEventListener() or something similar to capture errors, they do see these errors. This was a problem for our team as Sentry reported hundreds of these errors happening, but it took us a long time to eventually replicate them, because they don't get logged.

You can manually log them by running window.addEventListener('error', function(e) { console.error(e); }); in the developer tools, and all further events will be printed to the console.

The solution that worked for us:

The root of this problem seems to be React re-rendering (or any computationally intensive work, really) happening as a direct result of the resize event. If you defer that work to after the resize event has been handled, the browser is totally fine with it because it's not being prevented from delivering all of its event notifications in time. The simplest method of doing this for us was to replace:

dispatch({
  payload: {
    desktop,
    mobile,
    tablet,
  },
  type: "CHANGE_BREAKPOINTS",
});

with:

setTimeout(() => {
  dispatch({
    payload: {
      desktop,
      mobile,
      tablet,
    },
    type: "CHANGE_BREAKPOINTS",
  });
}, 0);

in our app. This defers the React update until the next pass of the event loop, gets it out of the event context, and avoids the issue. Obviously the code your app is running to trigger re-renders or other work will be different, but I believe that idea of running this work in a delayed callback should resolve the issue for any application.

I believe this also explains why requestAnimationFrame() also worked as a fix (albeit with side effects) - it also defers execution of the callback until after the current event.

I'm not sure if adding a setTimeout to the library would be an appropriate fix, as it may introduce similar bugs - but at least as a user of the library my team has found that this prevents the issue in our app and we felt it would be helpful to share.

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024 10

Many things changed in my life in the past five years... Except for the one - this bug is still open.

Today I'm going to change this too :)

I spent a day reading the comments and playing with the code. Honestly, I'm not sure I fully understand the root of the problem, so I had to almost blindly change this and that...

I believe the issue is fixed in React@18. To be more specific, the error goes when you update react-dom version to v18.

This is the fork of the sandbox provided in the message above. As you can see, there is no error https://codesandbox.io/s/useresizedetector-resizeobserver-loop-limit-exceeded-reprod-forked-v90h07

I hope all collaborators of this issue doing well ❀️ Letting you know that I will remember your github logins for a long time 🀣

And no, I won't reopen it! Sorry :)

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024 7

Sorry, guys, I have no quick fix for this problem.

Earlier we blindly added requestAnimationFrame without understanding the problem, which caused other bugs to appear.


For now we can try to workaround the problem by using refreshMode/refreshRate props:

{ refreshMode: 'debounce', refreshRate: 0 }

Any help would be greatly appreciated. Even if someone could provide a code snippet where the bug appears.

from react-resize-detector.

ksocha avatar ksocha commented on May 30, 2024 1

@maslianok
I've encountered similar issue with the latest version of react-resize-detector (v.6.6.5). I can also see that tis version doesn't use requestAnimationFrame function when firing onResize callback.

Is this something that should be fixed?

from react-resize-detector.

josias-r avatar josias-r commented on May 30, 2024 1

@maslianok I'm sure you have a reproduction case by now. But still, here's a very very basic example where the error already appears, simply by unmounting a resize-detector component using useState:
https://codesandbox.io/s/useresizedetector-resizeobserver-loop-limit-exceeded-reprod-psv67?file=/src/App.js

Screen.Recording.2022-01-20.at.11.54.21.mov

ResizeObserver loop completed with undelivered notifications. on FIrefox

from react-resize-detector.

ashen-dawn avatar ashen-dawn commented on May 30, 2024 1

@josias-r

Out of curiosity, do you have some more info on this statement:

events on the window object, these normally don't get logged.

So the ResizeObserver spec section 3.4.6 says that if there are undelivered resize notifications it should:

  1. Create a new ErrorEvent.
  2. Initialize event’s message slot to "ResizeObserver loop completed with undelivered notifications.".
  3. Report the exception event.

Reporting the exception is a bit confusing to follow, but I believe the relevant behavior is defined in the HTML spec section 8.1.4.6, where it says:

When the user agent is to report an exception E, the user agent must report the error for the relevant script [. . .] using the global object specified by the script's settings object as the target. If the error is still not handled after this, then the error may be reported to a developer console.

Error reporting includes:

If target implements EventTarget, then set notHandled to the result of firing an event named error at target

This appears to be why an event is emitted from the window object (which as far as I understand should always be the global object for scripts not happening in a web worker context). It not being logged to the console seems to happen because the error event is considered handled somewhere along the way - where or why that happens I have been unable to determine.

There's also the possibility I've found the wrong exception reporting spec, especially because the ResizeObserver spec indicates that the resize notification delivery (and undelivered notification exception) all happen as part of the browser's HTML processing loop rather than in any direct script context. I haven't managed to find anywhere else that talks about reporting an exception though, so at the moment I think this is accurate.

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024

Thanks for such detailed issue and links how to fix this.

Fixed in this commit 9625f67#diff-1fdf421c05c1140f6d71444ea2b27638R36

from react-resize-detector.

jsakas avatar jsakas commented on May 30, 2024

@ksocha @maslianok We have seen this issue for quite some time (at least since 6.1). It's visible in our Sentry logs and also Cypress fails on this error (unless we ignore it).

from react-resize-detector.

jsakas avatar jsakas commented on May 30, 2024

Also should note that changing refresh mode to debounce seems to resolve the problem.

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024

@ksocha @jsakas Thanks for reporting it. I'm going to reopen the issue

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024

To provide some context:

i've removed requestAnimationFrame here #122

requestAnimationFrame prevented the library to work properly when it was initialized in the background.

To fix this bug we have to reproduce it. Can you provide a reproducible example? Actually any info would help: browser version, device type (desktop or mobile), maybe some specific platform (Android / iOS / Windows / Linux / Mac) etc.

For now, the best workaround I can think about is to use debounce with 0 delay

from react-resize-detector.

khigh-figure avatar khigh-figure commented on May 30, 2024

Here's a list of user agent strings producing this error over the last week or so:

https://docs.google.com/spreadsheets/d/177wudO9omK6tbzyWmXKLFQpW1idKaceX4rzvb6MFxBE/edit?usp=sharing

from react-resize-detector.

cichyadam avatar cichyadam commented on May 30, 2024

+1

from react-resize-detector.

johnhunter avatar johnhunter commented on May 30, 2024

We're seeing this on the following Browser / OS:

Edge 90.0.818 / Windows 10
Edge 89.0.774 / Windows 10
Chrome 90.0.4430 / Mac OS X 10.15.7
Chrome 89.0.4389 / Mac OS X 11.2.3
Chrome 89.0.4389 / Mac OS X 10.15.7
Chrome 89.0.4389 / Windows 10
Chrome 88.0.4324 / Mac OS X 10.15.7
Chrome 88.0.4324 / Windows 10
Chrome 86.0.4240 / Windows 10
Chrome 84.0.4147 / Windows 8.1

from react-resize-detector.

joshuaball avatar joshuaball commented on May 30, 2024

+1

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024

@pawnstar oh wow!
I can't imagine how much time did you spend just to understand the root of the problem!

Thank you for such a detailed description πŸ‘ Great job πŸ’ͺ

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024

I'm not sure if adding a setTimeout to the library would be an appropriate fix, as it may introduce similar bugs

Yes, using setTimeout by default causes other bugs. Well, not bugs but unexpected behaviors.

I think you can use <ReactResizeDetector refreshMode="debounce" refreshRate={0}>. It should be the same as using setTimeout(..., 0) in the callback function.

from react-resize-detector.

jokklan avatar jokklan commented on May 30, 2024

@pawnstar @maslianok just to be clear; using any value as refreshRate (or any duration in setTimeout in the onResize callback) should work right? The important part is to defer the function call to later?

So values above 0, and both "debounce" and "throttle" should work as a workaround, correct?

from react-resize-detector.

maslianok avatar maslianok commented on May 30, 2024

@pawnstar @maslianok just to be clear; using any value as refreshRate (or any duration in setTimeout in the onResize callback) should work right? The important part is to defer the function call to later?

So values above 0, and both "debounce" and "throttle" should work as a workaround, correct?

yes, correct

from react-resize-detector.

johnhunter avatar johnhunter commented on May 30, 2024

Still having this issue despite using the suggested workaround. Will feedback if I find anything useful.

I'm not sure if the browser eventually does notify all the listeners and this warning just means there was a dropped frame, or if some of them just get skipped for that event? If it's the former then this is potentially an error that is safe to ignore, if it's the latter than probably not.

It should be the former if implemented to spec: Elements with undelivered notifications will be considered for delivery in the next loop. https://www.w3.org/TR/resize-observer/#html-event-loop

from react-resize-detector.

josias-r avatar josias-r commented on May 30, 2024

@aderickson Wow I was pulling my hair out trying to replicate it. Funny enough, the chrome instance of Cypress did log this error in the console as well, I guess they attach a similar event handler to log errors.

Out of curiosity, do you have some more info on this statement:

events on the window object, these normally don't get logged.

I've been in WebDev for a while now and was completely unaware of this. Can't even seem to find more insights on this online, MDN does not mention anything about it either

from react-resize-detector.

josias-r avatar josias-r commented on May 30, 2024

@aderickson Thanks a lot! I rarely have to go this deep into specs, so this is very interesting to me πŸ˜„

from react-resize-detector.

k-funk avatar k-funk commented on May 30, 2024

I think you can use <ReactResizeDetector refreshMode="debounce" refreshRate={0}>. It should be the same as using setTimeout(..., 0) in the callback function.

If this is still the recommended solution for a 2018 issue, should these two props be default arguments? My solution right now is using a wrapper component for my project's 30+ usages of <ReactResizeDetector />. Far from ideal.

export function ReactResizeDetectorWrapper(props) {
  // https://github.com/maslianok/react-resize-detector/issues/45
  return (
    <ReactResizeDetector refreshMode="debounce" refreshRate={0} {...props}>
      {props.children}
    </ReactResizeDetector>
  )
}

from react-resize-detector.

danieltroger avatar danieltroger commented on May 30, 2024

It not being logged to the console seems to happen because the error event is considered handled somewhere along the way - where or why that happens I have been unable to determine.

I created a browser extension that runs the following code which blocks adding an error event handler before there's anything else in the DOM (so no other scripts could have executed):

window.addEventListener = new Proxy(window.addEventListener, {
  apply(target, this_arg, arg_list) {
    if (arg_list[0] === "error") {
      console.log("blocked aev", arg_list, this_arg);
      console.trace();
      return;
    }
    return Reflect.apply(target, this_arg, arg_list);
  },
});
Object.defineProperty(window, "onerror", {
  get() {},
  set() {
    console.log("prevented setting onerror");
    console.trace();
  },
  configurable: false,
});

I did catch some libraries trying to add an error event handler but I don't see any error in both firefox's and chrome's consoles with no "error" event handlers. So I am confident to say that the browsers don't report this properly to the console and the only way to get the error is by having an "error" event handler.

The incredibly frustrating thing is that the error doesn't have any stack trace so I don't know which ResizeObserver is causing the issue for me (I'm not using this library, just came across this github issue since it contained good info about the ResizeObserver issue)

from react-resize-detector.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.