React: ResizeObserver loop completed with undelivered notifications
Asked Answered
W

16

72

I'm seeing the following error when I dismiss a particular modal:

Error message from React

This didn't happen when I was using react-router v5, but I've recently updated to v6 due to other requirements and it started happening. Funny thing is, I have a second page that invokes the same dialog and it doesn't trigger the error.

(Note that this is a development environment, not a production build. It might not appear in production, or just go to the console.)

I can't seem to find anything through googling... the closest I get are references to "ResizeObserver - loop limit exceeded". I'm puzzled by this, since it doesn't occur on the other page. To make matters worse, the application is using v3 of Bootstrap (company legacy reasons, I don't have the bandwidth right now to address that).

Any ideas?

Weddle answered 6/5, 2023 at 5:19 Comment(3)
react-router-dom hasn't much to do with any actual UI rendering (it matches a route to the URL path so your UI can render), so I suspect this issue with any resize observer is elsewhere. We can't help address issues in code we can't see though, so please do edit to include a minimal reproducible example of the relevant code you have issue working with and provide the error message and any code stacktrace as plain formatted test instead a picture of text. Images are less accessible, can be more difficult to read, and are not copy/pasteable.Charioteer
Unfortunately, the code is all company-internal. And while I am well-aware of the role of react-router-dom, that was just the only thing that had changed (the component itself had not). Anyway, I was able to isolate it to a single field in the form that the dialog was presenting. A field that Lastpass was trying to offer an autofill option for. Prevent LP from putting its icon in the text field solved the issue.Weddle
The request wasn't for internal, private, company code, it was for example code that reproduces the issue you face. Sounds like you found a workable solution though. Cheers.Charioteer
W
55

The dialog in question was rendering a form, and by removing one element at a time I was able to determine that one specific text-entry field was causing the issue.

The problem stemmed from the LastPass browser extension trying to provide the option of auto-fill for that field. If I shortened the width of the field, it no longer caused the error. If I disabled the LP auto-fill icon from appearing, it also solved the issue. I chose the latter, since this was not something that LP could really auto-fill anyway.

I don't know why this only triggered when I upgraded from v5 to v6 of react-router-dom. Our staging and production instances that have not yet received the upgraded code work fine with the LP icon present in the field. Nonetheless, the issue is fixed for me.

Weddle answered 6/5, 2023 at 17:23 Comment(5)
I had same issue and removing LastPass browser extension fix the issue. You saved my day !!!Rafi
how did you disable the LP auto-fill icon from appearing?Cousin
stackoverflow.com/users/6421/rjray can you explain how you disabled the LP auto-fill please. thxsCapsulate
Unfortunately, it doesn't seem to work like it used to; I used to add data-lpignore="true" to the list of attributes but that no longer works. In the case above I changed the text label associated with the field so that LP no longer interpreted it as a user name.Weddle
I get the impression from the docs that data-lpignore="true" will only work if the user enables an option in advanced settings How do I prevent fields from being filled automatically?Proline
F
17

I've stumbled upon a similar problem, though not quite related to react-router per se.

The specs say that this happens in case of infinite resize loops as it's displayed here.

To prevent this from happening, we've figured out using a cunning mediator function, which utilizes window.requestAnimationFrame() to render things properly.

Typescript code:

    const observerCallback: ResizeObserverCallback = (entries: ResizeObserverEntry[]) => {
      window.requestAnimationFrame((): void | undefined => {
        if (!Array.isArray(entries) || !entries.length) {
          return;
        }
        yourResizeHandler();
      });
    };
    const resizeObserver = new ResizeObserver(observerCallback);
Farny answered 18/7, 2023 at 15:41 Comment(2)
Unfortunately I can tell you from experience this solution can and does still raise exceptions. Specifically I'm calling requestAnimationFrame() exactly as per you code in our Clibu Notes app and can still get the same error.Basis
I tried this approach and in our case it actually did reduce the errors being logged, so it's worth a try.Incomprehension
T
9

If you aren't using ResizeObserver in your code, you shouldn't worry about it, it will not appear in build version of your app.

If you are using ResizeObserver check this to get a better understanding of the causes of the error Observation Errors:

Implementations following the specification invoke resize events before paint (that is, before the frame is presented to the user). If there was any resize event, style and layout are re-evaluated — which in turn may trigger more resize events. Infinite loops from cyclic dependencies are addressed by only processing elements deeper in the DOM during each iteration. Resize events that don't meet that condition are deferred to the next paint

This basically means the event of resize happens before the paint and if you do a resize to something that causes another event (before the paint) you could cause an infinite loop.

You can solve that by delaying "Your resize" so that it happens after the repaint, however if there are multiple resize events some of them will happen before the paint (caused by an old delayed resize).

You can avoid this by de-bouncing events and only doing a reaction to the last one (Cancel all your resizes until the observed element "settles").

Here an example on how to solve this in React

 // Here we are trying to match the size of div to an observed div
 // it will affect the origianl one if it is inside it or next to it...
 const observedDivRef = useRef(null);
 const resizingDelayTimer = useRef(null);
 useEffect(() => {
     const observer = new ResizeObserver(() => {
       clearTimeout(resizingDelayTimer.current);
       resizingDelayTimer.current = setTimeout(() => {
         // check if the observed div is still mounted
         // else this will cause memory leak   
         if (observedDivRef.current) {
      setMatchingDivWidth(observedDivRef.current.clientWidth)
         }
       }, 100)
       
     });
     observer.observe(observedDivRef);
   return () => {
     if (observer && observedDivRef.current) observer.unobserve(observedDivRef.current);
   }
 }, []);    
Taddeo answered 2/12, 2023 at 17:21 Comment(0)
M
6

You can find an explanation for this exact error in the documentation

https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors

So from my understanding, that error means that you have a case when the browser got so many resize events that it doesn't have a chance for an actual repaint, even though the layout has been changed. I fixed that problem by wrapping callback with debounce from Lodash with 0 timeout.

new ResizeOserver(debounce((entries) => {
  for (const entry of entries) {
     // enter code here
  }
}))
Mead answered 18/12, 2023 at 16:50 Comment(0)
S
5

Add the below line to your main CSS file like app.css or app.scss

iframe#webpack-dev-server-client-overlay{display:none!important}
Scalade answered 13/9, 2023 at 4:32 Comment(3)
This is imo a somewhat brute-force solution, but nevertheless it works and avoids having to click away the error messages. If somebody knows how to get rid of the original problem in a decent way, I'd be happy to learn about it.Radie
This does not address the root cause. This just hides the error overlay.Incline
This isn't a helpful answer, you haven't explained the massive caveat that you won't see the error popup for any other errors. And it doesn't solve the problem, you wouldn't see the error overlay in prod anyway..Bornholm
A
4

For me the issue happens with Cascader from Ant design and only in webpack dev server. Sujen's solution works, but it is a kill switch from all the webpack errors. There is more elegant way to hide them optionally on the webpack side. Edit file webpack.config.js to hide runtimeErrors only:

module.exports = { 
  //...
  devServer: {
    client: {
      overlay: {
        runtimeErrors: false,
      },
    },
  },
};

or even better - forward the error to the console:

module.exports = {
  //...
  devServer: {
    client: {
      overlay: {
        runtimeErrors: (error) => {
          if(error?.message === "ResizeObserver loop completed with undelivered notifications.")
          {
             console.error(error)
             return false;
          }
          return true;
        },
      },
    },
  },
};
Algonquian answered 31/1, 2024 at 16:36 Comment(4)
hiding the errors is your solution?Limulus
my solution is to catch this specific error in the webpack and forward it somewhere elseAlgonquian
For my case where this error only showed up in Cypress tests which may click things too fast, this is great. The consensus is indeed to ignore these ResizeObserver errors: #49384620 github.com/cypress-io/cypress/issues/22129Lungan
I'm doing if (error?.message.includes('ResizeObserver loop')) {Quagga
S
1

I had encountered this issue, upon further investigation of my code it turned out that, even though, I had updated to the latest version of React, I hadn't refactored the JS in my App.js file.

I simply resolved by updating my App.js file to the below

const container = document.getElementById("root");
const root = createRoot(container!);

root.render(<App />);
Subterfuge answered 6/11, 2023 at 12:9 Comment(3)
Can you expand on this? Why did this help you, and why could it help us? We don't use .render function...Numerical
I found this too. However root.render seems to render a node asynchronously, I still need the former apiMakassar
I launched an old project that was not mine on react 17 and encountered the same error upon startup. I updated react and react-dom to version 18, accordingly I had to update the index.js file as in this answer - and everything workedChalcedony
P
1

This hook solved my problem.

const useHideUnimportantErrors = () => {
useEffect(() => {
    function hideError(e) {
        if (e.message === 'ResizeObserver loop completed with undelivered notifications.') {
            const resizeObserverErrDiv = document.getElementById(
                'webpack-dev-server-client-overlay-div'
            );
            const resizeObserverErr = document.getElementById(
                'webpack-dev-server-client-overlay'
            );
            if (resizeObserverErr) {
                resizeObserverErr.setAttribute('style', 'display: none');
            }
            if (resizeObserverErrDiv) {
                resizeObserverErrDiv.setAttribute('style', 'display: none');
            }
        }
    }

    window.addEventListener('error', hideError)
    return () => {
        window.addEventListener('error', hideError)
    }
}, [])

}

Parthenia answered 9/6, 2024 at 5:12 Comment(0)
W
0

I had this issue due to an expensive callback function and a few DOM changes happening in quick succession. Debouncing it fixed the problem.

 useEffect(() => {
    const observeTarget = ref.current;
    if (!observeTarget) return;

    const debouncedCallback = debounce((entry) => {
      setElementSize({ width: entry.contentRect.width, height: entry.contentRect.height });
      if (callback && typeof callback === 'function') {
        callback(entry.contentRect);
      }
    }, debounceTime);

    const resizeObserver = new ResizeObserver((entries) => {
      if (!Array.isArray(entries) || !entries.length) return;

      const entry = entries[0];
      debouncedCallback(entry);
    });

    resizeObserver.observe(observeTarget);

    return () => resizeObserver.unobserve(observeTarget);
  }, [ref, callback, debounceTime]);
Wilkerson answered 17/1, 2024 at 18:27 Comment(0)
P
0

In my case, I just need to set the min-width for the body element in the "index.css" file.

body {
    min-width: 400px;
}
Parthenia answered 21/1, 2024 at 4:11 Comment(0)
H
0

I had the same issue using SyncFusion's RichTextEditor. Whenever I wrote enough characters so that the editor grew, and then deleted some lines, it resized and this error was fired.

Figured out it wasn't because of React or because of the component itself, but because I was using Firefox with the extension "LanguageTool", a grammar checker (thus observing textareas...).

If someone else is having the same issue, try using another navigator and maybe disable your plugins; one of them might be the culprit...

Hiramhirasuna answered 22/2, 2024 at 16:25 Comment(0)
D
0

I was able to fix the ResizeObserver completed with undelivered notifications error by writing local ResizeObserver hook (I'm using Typescript with React, react-router v6, react v17), the code is below.

I have a feeling that this error happens if calling ResizeObserver without checking for ref && ref.current.

In the hook, I return only the height and width of contentBoxSize, but you can modify the return to your needs. I'm using ResizeObserver to dynamically adjust the size of the container with a pdf file in it (I didn't include the pdf-related code, only the hook and a parent component).

type ResizeObserverReturnType = {
  height: number | undefined;
  width: number | undefined;
}

// The ResizeObserver hook
export const useResizeObserver = (
  ref: React.MutableRefObject<HTMLDivElement | null>
): ResizeObserverReturnType => {
  const [height, setHeight] = useState<number | undefined>(undefined);
  const [width, setWidth] = useState<number | undefined>(undefined);

  const observer: ResizeObserver = useMemo(() => {
    return new ResizeObserver(([entry]) => {
      const height = entry.contentBoxSize[0].blockSize;
      const width = entry.contentBoxSize[0].inlineSize;
      setHeight(height);
      setWidth(width);
    });
  }, []);

  useEffect(() => {
    if (ref && ref.current) {
      observer.observe(ref.current);
    }
    // Remove the observer as soon as the component is unmounted
    return () => {
      observer.disconnect();
    };
  }, [observer, ref]);

  return { height, width };
};

And then in my component I use this hook by providing a ref to it:

type Props = {
  testValue: string;
};
export const YourContainer: React.FC<Props> = props => {
  // Create ref
  const ref = useRef<HTMLDivElement | null>(null);
  // Pass ref to observer
  const { height = 1, width = 1 } = useResizeObserver(ref);

  // Connect ref to your container
  return (
    <StyledContainer ref={ref}>
      {
        // Render a component that is changing in size
        // Pass required width and/or height
        <YourComponent width={width} height={height} />
      }
    </StyledContainer>
  );
};
Drubbing answered 27/2, 2024 at 0:58 Comment(0)
N
0

I realized that LastPass, in its zeal to manage form inputs, was inadvertently intensifying the situation. Each dynamically generated form input was like a new battlefield for ResizeObserver and LastPass, creating a loop of updates that pushed ResizeObserver to its limits. One of the many temporary solution is to disable the LastPass extension from your browser. However, in my sitemaster.js file, I handled this exception this way.

window.alert = function (message) {
const keywordToIgnore = "resizeobserver";

const trimmedMessage = message.trim().toLowerCase();

if (trimmedMessage.includes(keywordToIgnore)) {
    return;
}

swal({
    html: message,
    type: "info",
}).then(
    function () {},
    function () {}
);

};

Nesline answered 8/3, 2024 at 10:57 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Virtues
W
0

I tried all solution , but these all are failed.

  useEffect(() => {
const errorHandler = (e:any) => {
  if (
    e.message.includes(
      "ResizeObserver loop completed with undelivered notifications" ||
        "ResizeObserver loop limit exceeded"
    )
  ) {
    const resizeObserverErr = document.getElementById(
      "webpack-dev-server-client-overlay"
    );
    if (resizeObserverErr) {
      resizeObserverErr.style.display = "none";
    }
  }
};
window.addEventListener("error", errorHandler);
return () => {
  window.removeEventListener("error", errorHandler);
};

}, [])

Whitesmith answered 22/3, 2024 at 3:6 Comment(0)
G
0

In my case, I was using the TextField component from Material UI with the options minRows and maxRows. However, when the content changed, this caused the component and its parent components to resize. My solution was to remove the minRows and maxRows options and instead use the rows option exclusively.

Gait answered 9/5, 2024 at 3:22 Comment(0)
J
0

I am using DevExtreme (UI-Framework) for an Angular Application. I was able to fix the issue by using shorter labels and item names for my RadioGroups.

in component.html:

<dxi-item
    [colSpan]="3"
    dataField="dataField"
    editorType="dxRadioGroup"
    [editorOptions]="{ items: myItems, valueExpr: 'key', displayExpr: 'name', layout: 'horizontal' }"
>
    <dxo-label text="ShortLabel needs to be short!"></dxo-label>
</dxi-item>

in component.ts:

myItems = [
  { key: false, name: "true" },
  { key: true, name: "false" },
];
Johannisberger answered 18/6, 2024 at 12:5 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.