Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear trackWrites on destroy to prevent memory leak #172

Closed
wants to merge 1 commit into from

Conversation

schontz
Copy link

@schontz schontz commented Sep 10, 2024

I did some memory profiling and came across an orphaned editor in a detached DOM node. It looks to be related to trackWrites:

Screenshot 2024-09-10 at 12 08 13 PM

This patch clears trackWrites on destroy to prevent this case.

@marijnh
Copy link
Member

marijnh commented Sep 10, 2024

This is a link from the editor object, which you supposedly dropped references to, to a DOM node. I do not see how this would be able to introduce a leak. Do you have an example script that demonstrates this?

@schontz
Copy link
Author

schontz commented Sep 10, 2024

I'll see if I can make a minimal repro of it. I was debugging our app in production, which uses tiptap on top of prose mirror.

@schontz
Copy link
Author

schontz commented Sep 25, 2024

The root cause of the leak I was experiencing was due to tiptap code: ueberdosis/tiptap#5654

But there is still an interesting condition that we may want to address here that actually is not related to destroying. This code appears to run before the actual editor is created, such that it grabs DOM nodes that may get replaced by the editor:

let chromeKludge = browser.chrome ? (this.trackWrites = this.domSelectionRange().focusNode) : null

In the linked ticket, I added a timeout to check on the kludge on the next tick:

setTimeout(() => console.log('Is trackWrites still in the doc?', this.trackWrites?.getRootNode()?.isConnected));

It was logging false whenever a new editor was replacing an old editor at the same location in the DOM and the cursor was in the old editor.

I switched trackWrites to use WeakRef and it fixed the issue of referencing an orphaned DOM node.

trackWrites = null;
get trackWrites() { return this.trackWritesRef?.deref(); }
set trackWrites(value) {
  if (value) this.trackWritesRef = new WeakRef(value);
  else this.trackWritesRef = null;
}

WeakRef is well-supported, but clearing trackWrites if it is disconnected via the immediate timeout should also suffice. And clearing it on destroy is also a good practice, so we are not hanging onto DOM nodes any longer than we have to.

@schontz schontz closed this Sep 25, 2024
@schontz
Copy link
Author

schontz commented Sep 25, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants