Skip to content

chore: Version updates for v4.0.0 - #371

Merged
camdecoster merged 26 commits into
masterfrom
release-4.0.0
Jun 18, 2026
Merged

chore: Version updates for v4.0.0#371
camdecoster merged 26 commits into
masterfrom
release-4.0.0

Conversation

@camdecoster

Copy link
Copy Markdown
Contributor

Description

Version updates for v4.0.0.

Closes

@KoolADE85KoolADE85 self-assigned this Jun 10, 2026

@KoolADE85KoolADE85 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is generally looking great, such a nice modernization. The typescript hints look great in my IDE!
I noticed one bug that seems fairly serious.

Comment threadsrc/factory.js Outdated
Comment on lines +166 to +176
const el = elRef.current;
if (el) {
if (typeof onPurgeRef.current === 'function') {
const frames = el._transitionData ? el._transitionData._frames : null;
onPurgeRef.current({data: el.data, layout: el.layout, frames}, el);
}
if (el.removeListener) {
updateEvents.forEach((evt) => el.removeListener(evt, handleUpdate));
}
Plotly.purge(el);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the time React calls this effect, it has already set elRef.current to be null, which means this Plotly.purge will not happen.

Repro example:

  1. Load the snippet into your browser
  2. Zoom into the graph
  3. Unmount the graph (zoom should be saved into state)
  4. Re-mount the graph (zoom should be preserved)
import{useRef,useState}from'react';importPlotfrom'react-plotly.js';constdata=[{type: 'scatter',mode: 'markers',x: [1,2,3,4],y: [2,4,1,3]}];functionZoomPersistence(){const[mounted,setMounted]=useState(true);constsavedLayout=useRef(null);return(<><buttononClick={()=>setMounted((m)=>!m)}>{mounted ? 'Unmount' : 'Remount'}</button>{mounted&&(<Plotdata={data}// restore the state captured on last unmount, if anylayout={savedLayout.current??{width: 500,height: 300}}// capture figure state during unmountonPurge={(figure)=>(savedLayout.current=figure.layout)}/>)}</>);}

Further Consequences (according to Claude)

  • Repeated mount/unmount of WebGL plots can exhaust the browser's context limit
  • With config: {responsive: true}, the window resize listener (only removed by purge) now pins every unmounted graph div in memory permanently.

@camdecostercamdecoster changed the title chore: Version updates for v3.0.0chore: Version updates for v4.0.0Jun 12, 2026

@KoolADE85KoolADE85 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💃

@camdecoster
camdecoster merged commit ea168dc into masterJun 18, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment