Skip to content

Maximum update depth exceeded when chart unmounts behind Suspense boundary (React 19) #7463

Description

@ChrisFFRR

Charts throw an infinite setState loop when they're in a subtree hidden by a Suspense boundary in React 19.

Environment:

  • recharts: 3.8.1 (also 3.8.0, 3.7.0 — tested all three)
  • react: 19.2.7
  • react-dom: 19.2.7

React 19 introduced disappearLayoutEffects — when a Suspense boundary triggers, React runs ref cleanup (safelyDetachRef) on the hidden subtree instead of fully unmounting it. The innerRef callback in RechartsWrapper calls setTooltipPortal(node) and setLegendPortal(node) unconditionally, including when called with null during cleanup. Calling setState inside a ref cleanup during disappearLayoutEffects causes React to loop.

Stack trace (abbreviated):
Maximum update depth exceeded...
at RechartsWrapper.js:213
at safelyDetachRef (react-dom-client.development.js:13529)
at disappearLayoutEffects (react-dom-client.development.js:15198)

The offending code (RechartsWrapper.js):
var innerRef = useCallback(node => {
setScaleRef(node);
ref?.(node);
setTooltipPortal(node); // called with null during Suspense cleanup → triggers re-render
setLegendPortal(node); // same
...
}, [...]);

Fix direction:

Guard the setState calls so they only fire when node is not null, or use a ref instead of state for portal targets:

if (node !== null) {
setTooltipPortal(node);
setLegendPortal(node);
}

Workaround:

Set useSuspense: false in i18n.ts config. Works
but undesirable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions