radix-ui / primitives

Radix Primitives is an open-source UI component library for building high-quality, accessible design systems and web apps. Maintained by @workos.
https://radix-ui.com/primitives
MIT License
15.63k stars 800 forks source link

`useSize` throws `ResizeObserver loop completed with undelivered notifications` error #2313

Open Pearce-Ropion opened 1 year ago

Pearce-Ropion commented 1 year ago

Bug report

Current Behavior

The useSize hook in @radix-ui/react-use-size has the potential to throw ResizeObserver loop completed with undelivered notifications errors when the ResizeObserver is not able to deliver all observations within a single animation frame. This can often happen in larger applications where a lot of code is being run during a mutation.

See https://github.com/WICG/resize-observer/issues/38

Expected behavior

useSize should complete without error

Reproducible example

Difficult to reproduce as it generally only happens in performance heavy applications

Suggested solution

We already solved this problem in useResizeObserver in @radix-ui/react-scroll-area where we wrap the resize observer in a requestAnimationFrame call. We should port the same logic to the the useSize hook.

See https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/src/ScrollArea.tsx#L991

We should probably extract the useResizeObserver and useDebounceCallback (used internally) to a separate package so that it can be reused in both locations.

Additional context

Your environment

I've included all radix packages that use useSize internally

Software Name(s) Version
Radix checkbox 1.0.4
Radix popper 1.1.2
Radix radio-group 1.1.3
Radix slide 1.1.2
Radix switch 1.0.1
Radix use-size 1.0.1
React 17
Browser Chrome 115.0.5790.114
Assistive tech - -
Node 18
npm/yarn yarn 1.19
Operating System MacOS 13.2.1 (22D68)
patrikmasiar commented 1 year ago

Same issue

vai0 commented 7 months ago

@Pearce-Ropion did you figure this out? :smiley:

Pearce-Ropion commented 7 months ago

@Pearce-Ropion did you figure this out? 😃

As a workaround, we added the following to our webpack dev-server config:

const webpackDevServerConfig = {
  client: {
    overlay: {
      runtimeErrors: error => {
        /**
         * This is a benign and seemingly impossible error to avoid, and it
         * appears sporadically.
         */
        if (error.message === 'ResizeObserver loop limit exceeded') {
          return false;
        }

        /**
         * Resize Observer will often throw this benign error which indicates
         * that ResizeObserver was not able to deliver all observations within
         * a single animation frame. In these cases, we should wrap our resize
         * observer callback in a `requestAnimationFrame` to ensure we don't
         * deliver unnecessary observations. However, we can't account for all
         * ResizeObserver usage in other libraries.
         * @see https://github.com/WICG/resize-observer/issues/38
         */
        if (
          error.message ===
          'ResizeObserver loop completed with undelivered notifications.'
        ) {
          return false;
        }

        return true;
      },
    },
  },
};