fix(@uppy/image-editor): destroy cropper and clean up state on file re-open and removal

When opening the image editor for a second file (e.g. drag file → save in
editor → clear → drag new file), the old Cropper instance was not destroyed.
This caused `initCropper()` to bail early (`if (this.cropper) return`),
leaving the new image with a stale cropper that retained the previous file's
crop dimensions and referenced a destroyed DOM element.

Additionally, when a file was removed from the Dashboard, the ImageEditor
plugin had no handler to clean up its internal state (currentImage, cropper,
objectUrl), so everything persisted into the next editing session.

Changes:
- Call `this.destroyCropper()` at the top of `start()` to tear down any
  previous cropper before initializing a new editing session
- Add a `file-removed` event listener (registered in `install()`, cleaned
  up in `uninstall()`) that calls `stop()` when the currently-edited file
  is removed from Uppy
This commit is contained in:
Alex Hixon 2026-03-13 13:08:20 +11:00
parent ddae349193
commit fe290e3001
2 changed files with 15 additions and 0 deletions

View file

@ -0,0 +1,5 @@
---
"@uppy/image-editor": patch
---
Fix cropper not reinitializing after save/cancel by destroying previous instance in `start()` and cleaning up on file removal

View file

@ -350,6 +350,7 @@ export default class ImageEditor<
*/
start = (file: UppyFile<M, B>): void => {
// Clean up any previous editing session
this.destroyCropper()
if (this.objectUrl) {
URL.revokeObjectURL(this.objectUrl)
this.objectUrl = null
@ -480,8 +481,16 @@ export default class ImageEditor<
return this.objectUrl
}
handleFileRemoved = (file: UppyFile<M, B>): void => {
const { currentImage } = this.getPluginState()
if (currentImage && currentImage.id === file.id) {
this.stop()
}
}
install(): void {
this.resetEditorState(null)
this.uppy.on('file-removed', this.handleFileRemoved)
const { target } = this.opts
if (target) {
@ -496,6 +505,7 @@ export default class ImageEditor<
const file = this.uppy.getFile(currentImage.id)
this.uppy.emit('file-editor:cancel', file)
}
this.uppy.off('file-removed', this.handleFileRemoved)
this.stop()
this.unmount()
}