Skip to content

Workspace context destroy() not called when navigating between sections #21272

@mattbrailsford

Description

@mattbrailsford

Summary

When navigating between backoffice sections (e.g., from Content to Media), workspace context APIs are not properly destroyed. The destroy() method on workspace context classes is never called, potentially causing memory leaks and preventing proper cleanup.

Steps to Reproduce

  1. Navigate to Content section and open a document
  2. Add a console.log or breakpoint in any workspace context's destroy() method
  3. Navigate to Media section (or any other section)
  4. Observe that destroy() is never called on the document workspace context

Expected Behavior

When navigating away from a workspace, the workspace context's destroy() method should be called to allow proper cleanup of subscriptions, event listeners, and other resources.

Actual Behavior

The destroy() method is never called. The workspace context instance remains in memory until the page is refreshed.

Note: destroy() IS correctly called when:

  • Closing a modal workspace (e.g., block editor)
  • The entityType property changes on the same workspace element

But NOT when:

  • Navigating to a different section entirely
  • The workspace element is removed from the DOM

Root Cause Analysis

Looking at workspace.element.ts, the UmbWorkspaceElement class does not override hostDisconnected() to destroy its extensions controller when the element is removed from the DOM.

Current code (src/packages/core/workspace/workspace.element.ts):

@customElement('umb-workspace')
export class UmbWorkspaceElement extends UmbLitElement {
    #extensionsController?: UmbExtensionsElementAndApiInitializer<any>;
    
    // ... entityType setter calls #createController which destroys old controller
    // BUT there's no hostDisconnected() override
}

The #createController method does destroy the old controller, but only when a NEW controller is being created. When navigating away entirely, the element is removed from DOM without triggering any cleanup.

Suggested Fix

Add a hostDisconnected() override to properly clean up:

override hostDisconnected(): void {
    super.hostDisconnected();
    if (this.#extensionsController) {
        this.#extensionsController.destroy();
        this.#extensionsController = undefined;
    }
}

This ensures the extensions controller (and thus the workspace context API) is properly destroyed when the workspace element is disconnected from the DOM.

Impact

  • Memory leaks: Workspace contexts and their subscriptions may accumulate
  • Stale state: Old workspace contexts may still be receiving updates
  • Third-party extensions: Extensions that rely on destroy() for cleanup won't work correctly

Environment

  • Umbraco version: 15.x / 17.x (affects current backoffice architecture)
  • Browser: All
  • OS: All

This item has been added to our backlog AB#63312

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions