Skip to content

Commit 2e3b042

Browse files
authored
fix(editor): clean up active animations, following, and menus on dispose (tldraw#8422)
In order to prevent stale state and leaked reactive subscriptions when editors are remounted (e.g. React strict mode, deep links), this PR adds cleanup for in-progress camera animations, user following, and open menus to `Editor.dispose()`. Relates to tldraw#8396. Previously, if the editor was disposed while a camera animation was running, a user was being followed, or menus were open, these resources would leak: - **Camera animations**: `stopCameraAnimation()` was never called, leaving tick listeners and `once('stop-camera-animation')` handlers attached to the EventEmitter. - **Following user**: The `react('update current page')` reactive subscription would continue running after dispose since `stopFollowingUser()` was never called — the most significant leak, as this is a live `EffectScheduler` that keeps reacting to store changes. - **Open menus**: Entries in the global `tlmenus` atom scoped to the disposed editor's context would persist indefinitely. ### Change type - [x] `bugfix` ### Test plan 1. In `apps/examples/src/index.tsx`, set `ENABLE_STRICT_MODE = true` 2. Run `yarn dev`, open any example 3. Start a camera animation (e.g. zoom to fit), then quickly navigate away and back 4. Verify the editor works correctly after remount 5. Follow a user in a multiplayer room, then dispose the editor — verify no console errors from stale reactive subscriptions - [x] Unit tests (existing tests pass — 774 editor + 2208 tldraw) ### Release notes - Fixed leaked camera animations, following subscriptions, and stale menu state when the editor is disposed during active operations. ### Code changes | Section | LOC change | | ---------- | ---------- | | Core code | +11 / -0 |
1 parent ee5a796 commit 2e3b042

1 file changed

Lines changed: 11 additions & 0 deletions

File tree

packages/editor/src/lib/editor/Editor.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,8 +1088,19 @@ export class Editor extends EventEmitter<TLEventMap> {
10881088
* @public
10891089
*/
10901090
dispose() {
1091+
// Stop any in-progress camera animations and following before
1092+
// running disposables, so their cleanup listeners fire first
1093+
this.stopCameraAnimation()
1094+
if (this.getInstanceState().followingUserId) {
1095+
this.stopFollowingUser()
1096+
}
1097+
10911098
this.disposables.forEach((dispose) => dispose())
10921099
this.disposables.clear()
1100+
1101+
// Clear any open menus for this editor's context
1102+
this.menus.clearOpenMenus()
1103+
10931104
this.store.dispose()
10941105
this.isDisposed = true
10951106
this.emit('dispose')

0 commit comments

Comments
 (0)