Skip to content

Conversation

@ilonatommy
Copy link
Member

@ilonatommy ilonatommy commented Nov 26, 2025

Fixes #64523.

Tests cleanup:

This PR reverts changes to tests architecture done in #64159 to avoid fixing multiple issues in one PR. It removes the additional test file and moves PersistentStateIsSupportedInDynamicJSRoots to PersistentState-connected test file.

Framework fixes:

The goal of the changes is to differentiate between the following scenarios:

Multi-Host / Different Renderer Types (Unsupported - Throws Error)

  1. Host A initializes: A Server-rendered Blazor application starts
  2. Circuit A connects: Server renderer (e.g., rendererId = 0)
  3. .NET calls enableJSRootComponents: With Server's rendererId and managerInstance
  4. JS state: manager is set, currentRendererId = 0
  5. Host B attempts initialization: On the same page, a WebAssembly Blazor host is also loaded
  6. WASM renderer calls enableJSRootComponents: With WebAssembly's rendererId (e.g., 1) and its own managerInstance.
  7. Exception thrown: "Dynamic root components have already been enabled."
  8. Developer responsibility: It's up to the developer to ensure only one host enables dynamic root components.

Same Renderer Type Circuit Restart (Supported)

  1. Initial state (after 1st iteration): Circuit 1 was previously connected and then shut down
  2. JS state: manager variable holds the old DotNetObjectReference from Circuit 1. currentRendererId holds the renderer ID (e.g., Server renderer = 0)
  3. Circuit 2 connects: A new Blazor Server circuit is created (same renderer type - Server)
  4. .NET calls enableJSRootComponents: The framework calls this with:
  • Same rendererId] (e.g., 0 for Server)
  • New managerInstance (a fresh DotNetObjectReference from Circuit 2)
  1. No exception thrown: the new managerInstance replaces the old stale one
  2. JS initializers skipped: hasInitializedJsComponents is already true, so initializers don't run again.
  3. Circuit 2 becomes interactive: Components can now use Blazor.rootComponents.add() with the new valid manager.

@ilonatommy ilonatommy added this to the .NET 11 Planning milestone Nov 26, 2025
@ilonatommy ilonatommy self-assigned this Nov 26, 2025
@ilonatommy ilonatommy requested a review from a team as a code owner November 26, 2025 09:04
Copilot AI review requested due to automatic review settings November 26, 2025 09:04
@ilonatommy ilonatommy added the area-blazor Includes: Blazor, Razor Components label Nov 26, 2025
Copilot finished reviewing on behalf of ilonatommy November 26, 2025 09:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enables JS root components to reinitialize when a Blazor circuit restarts, while still preventing multi-host scenarios. Previously, any attempt to re-enable JS root components would throw an error, even for valid circuit restart scenarios.

Key Changes:

  • Modified JS root component initialization logic to differentiate between circuit restarts (same renderer type) and multi-host scenarios (different renderer types)
  • Consolidated test coverage by moving dynamic JS root component tests into the main state persistence test suite
  • Removed configuration-based component registration in favor of always registering the test component

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Components/Web.JS/src/Rendering/JSRootComponents.ts Added currentRendererId and hasInitializedJsComponents tracking to support circuit restart while preventing multi-host scenarios
src/Components/Web.JS/src/Rendering/WebRendererInteropMethods.ts Updated enableJSRootComponents call to include rendererId parameter
src/Components/test/E2ETest/Tests/StatePersistenceTest.cs Added test for persistent state support in dynamic JS roots
src/Components/test/E2ETest/Tests/StatePersistanceJSRootTest.cs Removed separate test file (consolidated into StatePersistenceTest.cs)
src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs Simplified to always register dynamic JS root component instead of using configuration
src/Components/test/testassets/Components.TestServer/Program.cs Removed separate test scenario for JS root components

Comment on lines +140 to +152
if (!hasInitializedJsComponents) {
// Call the registered initializers. This is an arbitrary subset of the JS component types that are registered
// on the .NET side - just those of them that require some JS-side initialization (e.g., to register them
// as custom elements).
for (const [initializerIdentifier, componentIdentifiers] of Object.entries(jsComponentInitializers)) {
const initializerFunc = DotNet.findJSFunction(initializerIdentifier, 0) as JSComponentInitializerCallback;
for (const componentIdentifier of componentIdentifiers) {
const parameters = jsComponentParameters[componentIdentifier];
initializerFunc(componentIdentifier, parameters);
}
}

hasInitializedJsComponents = true;
Copy link
Member

Choose a reason for hiding this comment

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

I think there is potentially more to this than just fixing this error. Try and trigger a pause and a resume operation manually. There are likely other things that fail? (happy to be wrong through)

Copy link
Member Author

Choose a reason for hiding this comment

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

Update: the disconnection test should be done on JS root component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enabling JS root components breaks circuit reinitialization

2 participants