Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ All existing components can still be used with static SSR. However, the cost of

†There's a special exception for the `@onsubmit` event handler for forms, which is always functional, regardless of render mode.

This is equivalent to how components behave during [prerendering](xref:blazor/fundamentals/index#client-and-server-rendering-concepts), before a Blazor circuit or the .NET WebAssembly runtime is started.
This is equivalent to how components behave during [prerendering](xref:blazor/fundamentals/index#client-and-server-rendering-concepts), before Blazor's SignalR circuit or the .NET WebAssembly runtime is started.

For components whose only role is to produce read-only DOM content, these behaviors for static SSR are completely sufficient. However, library authors must consider what approach to take when including interactive components in their libraries.

Expand Down
8 changes: 4 additions & 4 deletions aspnetcore/blazor/components/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ The persisted prerendered state is transferred to the client, where it's used to

## Prerendered state size and SignalR message size limit

A large prerendered state size may exceed the SignalR circuit message size limit, which results in the following:
A large prerendered state size may exceed Blazor's SignalR circuit message size limit, which results in the following:

* The SignalR circuit fails to initialize with an error on the client: :::no-loc text="Circuit host not initialized.":::
* The reconnection UI on the client appears when the circuit fails. Recovery isn't possible.
Expand Down Expand Up @@ -1055,7 +1055,7 @@ The persisted prerendered state is transferred to the client, where it's used to

## Prerendered state size and SignalR message size limit

A large prerendered state size may exceed the SignalR circuit message size limit, which results in the following:
A large prerendered state size may exceed Blazor's SignalR circuit message size limit, which results in the following:

* The SignalR circuit fails to initialize with an error on the client: :::no-loc text="Circuit host not initialized.":::
* The reconnection UI on the client appears when the circuit fails. Recovery isn't possible.
Expand Down Expand Up @@ -1487,7 +1487,7 @@ For more information, see <xref:blazor/components/index#class-name-and-namespace

## Prerendered state size and SignalR message size limit

A large prerendered state size may exceed the SignalR circuit message size limit, which results in the following:
A large prerendered state size may exceed Blazor's SignalR circuit message size limit, which results in the following:

* The SignalR circuit fails to initialize with an error on the client: :::no-loc text="Circuit host not initialized.":::
* The reconnection UI on the client appears when the circuit fails. Recovery isn't possible.
Expand Down Expand Up @@ -1917,7 +1917,7 @@ For more information, see <xref:blazor/components/index#class-name-and-namespace

## Prerendered state size and SignalR message size limit

A large prerendered state size may exceed the SignalR circuit message size limit, which results in the following:
A large prerendered state size may exceed Blazor's SignalR circuit message size limit, which results in the following:

* The SignalR circuit fails to initialize with an error on the client: :::no-loc text="Circuit host not initialized.":::
* The reconnection UI on the client appears when the circuit fails. Recovery isn't possible.
Expand Down
9 changes: 9 additions & 0 deletions aspnetcore/blazor/components/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,15 @@ When [anonymous functions](/dotnet/csharp/programming-guide/statements-expressio

For more information, see [Cleaning up unmanaged resources](/dotnet/standard/garbage-collection/unmanaged) and the topics that follow it on implementing the `Dispose` and `DisposeAsync` methods.

### Disposal during JS interop

Trap <xref:Microsoft.JSInterop.JSDisconnectedException> in potential cases where loss of Blazor's SignalR circuit prevents JS interop calls and results an unhandled exception.

For more information, see the following resources:

* [JavaScript isolation in JavaScript modules](xref:blazor/js-interop/call-javascript-from-dotnet#javascript-isolation-in-javascript-modules)
* [JavaScript interop calls without a circuit](xref:blazor/js-interop/index#javascript-interop-calls-without-a-circuit)

## Cancelable background work

Components often perform long-running background work, such as making network calls (<xref:System.Net.Http.HttpClient>) and interacting with databases. It's desirable to stop the background work to conserve system resources in several situations. For example, background asynchronous operations don't automatically stop when a user navigates away from a component.
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/render-modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ In the following example, the `SharedMessage` component is interactive over a Si

In the following example, both `SharedMessage` components are prerendered and appear when the page is displayed in the browser.

* The first `SharedMessage` component with interactive server-side rendering (interactive SSR) is interactive after the SignalR circuit is established.
* The first `SharedMessage` component with interactive server-side rendering (interactive SSR) is interactive after Blazor's SignalR circuit is established.
* The second `SharedMessage` component with client-side rendering (CSR) is interactive *after* the Blazor app bundle is downloaded and the .NET runtime is active on the client.

`RenderMode7.razor`:
Expand Down
14 changes: 7 additions & 7 deletions aspnetcore/blazor/fundamentals/signalr.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ If prerendering is configured, prerendering occurs before the client connection

## Prerendered state size and SignalR message size limit

A large prerendered state size may exceed the SignalR circuit message size limit, which results in the following:
A large prerendered state size may exceed Blazor's SignalR circuit message size limit, which results in the following:

* The SignalR circuit fails to initialize with an error on the client: :::no-loc text="Circuit host not initialized.":::
* The reconnection UI on the client appears when the circuit fails. Recovery isn't possible.
Expand Down Expand Up @@ -768,25 +768,25 @@ Circuit activity handlers also provide an approach for accessing scoped Blazor s

:::moniker range=">= aspnetcore-8.0"

Configure the manual start of a Blazor app's SignalR circuit in the `App.razor` file of a Blazor Web App:
Configure the manual start of Blazor's SignalR circuit in the `App.razor` file of a Blazor Web App:

:::moniker-end

:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"

Configure the manual start of a Blazor app's SignalR circuit in the `Pages/_Host.cshtml` file (Blazor Server):
Configure the manual start of Blazor's SignalR circuit in the `Pages/_Host.cshtml` file (Blazor Server):

:::moniker-end

:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0"

Configure the manual start of a Blazor app's SignalR circuit in the `Pages/_Layout.cshtml` file (Blazor Server):
Configure the manual start of Blazor's SignalR circuit in the `Pages/_Layout.cshtml` file (Blazor Server):

:::moniker-end

:::moniker range="< aspnetcore-6.0"

Configure the manual start of a Blazor app's SignalR circuit in the `Pages/_Host.cshtml` file (Blazor Server):
Configure the manual start of Blazor's SignalR circuit in the `Pages/_Host.cshtml` file (Blazor Server):

:::moniker-end

Expand Down Expand Up @@ -1394,9 +1394,9 @@ protected override async Task OnInitializedAsync()

:::moniker range=">= aspnetcore-5.0"

## Disconnect the Blazor circuit from the client
## Disconnect Blazor's SignalR circuit from the client

A Blazor circuit is disconnected when the [`unload` page event](https://developer.mozilla.org/docs/Web/API/Window/unload_event) is triggered. To disconnect the circuit for other scenarios on the client, invoke `Blazor.disconnect` in the appropriate event handler. In the following example, the circuit is disconnected when the page is hidden ([`pagehide` event](https://developer.mozilla.org/docs/Web/API/Window/pagehide_event)):
Blazor's SignalR circuit is disconnected when the [`unload` page event](https://developer.mozilla.org/docs/Web/API/Window/unload_event) is triggered. To disconnect the circuit for other scenarios on the client, invoke `Blazor.disconnect` in the appropriate event handler. In the following example, the circuit is disconnected when the page is hidden ([`pagehide` event](https://developer.mozilla.org/docs/Web/API/Window/pagehide_event)):

```javascript
window.addEventListener('pagehide', () => {
Expand Down
8 changes: 7 additions & 1 deletion aspnetcore/blazor/hybrid/static-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,13 @@ In a Razor component:
{
if (module is not null)
{
await module.DisposeAsync();
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
Expand Down
42 changes: 0 additions & 42 deletions aspnetcore/blazor/includes/js-interop/circuit-disconnection.md

This file was deleted.

8 changes: 7 additions & 1 deletion aspnetcore/blazor/includes/js-interop/js-collocation.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,13 @@ The following `JsCollocation2` component's `OnAfterRenderAsync` method loads a J
{
if (module is not null)
{
await module.DisposeAsync();
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ When working with <xref:Microsoft.JSInterop.IJSObjectReference> in ASP.NET Core
{
if (module is not null)
{
await module.DisposeAsync();
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,13 @@ In the following component, the `Trigger JS function` buttons call JS functions
{
if (module is not null)
{
await module.DisposeAsync();
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}

dotNetHelper?.Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,48 @@ In the preceding example, the `{CONDITION}` placeholder represents a conditional

For browser compatibility, see [Can I use: JavaScript modules: dynamic import](https://caniuse.com/es6-module-dynamic-import).

For example, the following JS module exports a JS function for showing a [browser window prompt](https://developer.mozilla.org/docs/Web/API/Window/prompt). Place the following JS code in an external JS file.
In server-side scenarios, JS interop calls can't be issued after Blazor's SignalR circuit is disconnected. Without a circuit during component disposal or at any other time that a circuit doesn't exist, the following method calls fail and log a message that the circuit is disconnected as a <xref:Microsoft.JSInterop.JSDisconnectedException>:

* JS interop method calls
* <xref:Microsoft.JSInterop.IJSRuntime.InvokeAsync%2A?displayProperty=nameWithType>
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync%2A?displayProperty=nameWithType>
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync%2A?displayProperty=nameWithType>)
* `Dispose`/`DisposeAsync` calls on any <xref:Microsoft.JSInterop.IJSObjectReference>.

In order to avoid logging <xref:Microsoft.JSInterop.JSDisconnectedException> or to log custom information in server-side Blazor, catch the exception in a [`try-catch`](/dotnet/csharp/language-reference/keywords/try-catch) statement.

For the following component disposal example:

* The server-side component implements <xref:System.IAsyncDisposable>.
* `module` is an <xref:Microsoft.JSInterop.IJSObjectReference> for a JS module.
* <xref:Microsoft.JSInterop.JSDisconnectedException> is caught and not logged.
* Optionally, you can log custom information in the `catch` statement at whatever log level you prefer. The following example doesn't log custom information. The code assumes that the developer doesn't care about when or where circuits are disconnected during component disposal.

```csharp
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (module is not null)
{
await module.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
```

If you must clean up your own JS objects or execute other JS code on the client after a circuit is lost in a server-side Blazor app, use the [`MutationObserver`](https://developer.mozilla.org/docs/Web/API/MutationObserver) pattern in JS on the client. The `MutationObserver` pattern allows you to execute JS code when an element is removed from the DOM.

For more information, see the following articles:

* <xref:blazor/js-interop/index#dom-cleanup-tasks-during-component-disposal>: Includes a code example of the `MutationObserver` pattern.
* <xref:blazor/fundamentals/handle-errors#javascript-interop>: The *JavaScript interop* section discusses error handling in JS interop scenarios.
* <xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable>: The *Component disposal with `IDisposable` and `IAsyncDisposable`* section describes how to implement disposal patterns in Razor components.

The following JS module exports a JS function for showing a [browser window prompt](https://developer.mozilla.org/docs/Web/API/Window/prompt). Place the following JS code in an external JS file.

`wwwroot/scripts.js`:

Expand Down Expand Up @@ -1069,7 +1110,7 @@ In the preceding example:
* The `{TIMEOUT}` placeholder is a <xref:System.TimeSpan> (for example, `TimeSpan.FromSeconds(80)`).
* The `{ID}` placeholder is the identifier for the function to invoke. For example, the value `someScope.someFunction` invokes the function `window.someScope.someFunction`.

Although a common cause of JS interop failures are network failures with server-side components, per-invocation timeouts can be set for JS interop calls for client-side components. Although no SignalR circuit exists for a client-side component, JS interop calls might fail for other reasons that apply.
Although a common cause of JS interop failures are network failures with server-side components, per-invocation timeouts can be set for JS interop calls for client-side components. Although no Blazor-SignalR circuit exists for a client-side component, JS interop calls might fail for other reasons that apply.

For more information on resource exhaustion, see <xref:blazor/security/server/interactive-server-side-rendering>.

Expand Down
Loading
Loading