Skip to content

Merge to Live #35193

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

Merged
merged 9 commits into from
Apr 10, 2025
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
2 changes: 2 additions & 0 deletions aspnetcore/blazor/call-web-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,8 @@ To opt-out of response streaming globally, use either of the following approache

* Set the `DOTNET_WASM_ENABLE_STREAMING_RESPONSE` environment variable to `false` or `0`.

............. AND REMOVE THE NEXT LINE .............

-->

To opt-out of response streaming globally, set the `DOTNET_WASM_ENABLE_STREAMING_RESPONSE` environment variable to `false` or `0`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ else
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
"fetchdata", out var restored))
nameof(forecasts), out var restored))
{
forecasts = await WeatherForecastService.GetForecastAsync(
DateOnly.FromDateTime(DateTime.Now));
Expand All @@ -531,7 +531,7 @@ else

private Task PersistData()
{
ApplicationState.PersistAsJson("fetchdata", forecasts);
ApplicationState.PersistAsJson(nameof(forecasts), forecasts);

return Task.CompletedTask;
}
Expand Down Expand Up @@ -1030,7 +1030,7 @@ else
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
"fetchdata", out var restored))
nameof(forecasts), out var restored))
{
forecasts =
await WeatherForecastService.GetForecastAsync(DateTime.Now);
Expand All @@ -1046,7 +1046,7 @@ else

private Task PersistData()
{
ApplicationState.PersistAsJson("fetchdata", forecasts);
ApplicationState.PersistAsJson(nameof(forecasts), forecasts);

return Task.CompletedTask;
}
Expand Down
93 changes: 87 additions & 6 deletions aspnetcore/blazor/components/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,83 @@ In `Pages/_Host.cshtml` of Blazor apps that are `ServerPrerendered` in a Blazor
</body>
```

:::moniker-end

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

<!-- UPDATE 10.0 - API cross-link -->

Decide what state to persist using the <xref:Microsoft.AspNetCore.Components.PersistentComponentState> service. The `[SupplyParameterFromPersistentComponentState]` attribute applied to a property registers a callback to persist the state during prerendering and loads it when the component renders interactively or the service is instantiated.

In the following example, the `{TYPE}` placeholder represents the type of data to persist (for example, `WeatherForecast[]`).

```razor
@code {
[SupplyParameterFromPersistentComponentState]
public {TYPE} Data { get; set; }
}
```

In the following example, the `WeatherForecastPreserveState` component persists weather forecast state during prerendering and then retrieves the state to initialize the component. The [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) persists the component state after all component invocations.

`WeatherForecastPreserveState.razor`:

```razor
@page "/weather-forecast-preserve-state"
@using BlazorSample.Shared
@inject IWeatherForecastService WeatherForecastService

<PageTitle>Weather Forecast</PageTitle>

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (Forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in Forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}

@code {
[SupplyParameterFromPersistentComponentState]
public WeatherForecast[]? Forecasts { get; set; }

protected override async Task OnInitializedAsync()
{
Forecasts ??= await WeatherForecastService.GetForecastAsync(
DateOnly.FromDateTime(DateTime.Now));
}
}
```

:::moniker-end

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

Decide what state to persist using the <xref:Microsoft.AspNetCore.Components.PersistentComponentState> service. <xref:Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnPersisting%2A?displayProperty=nameWithType> registers a callback to persist the component state before the app is paused. The state is retrieved when the application resumes. Make the call at the end of initialization code in order to avoid a potential race condition during app shutdown.

In the following example:
Expand Down Expand Up @@ -449,7 +526,7 @@ In the following example:
}
```

The following example is an updated version of the `FetchData` component based on the Blazor project template. The `WeatherForecastPreserveState` component persists weather forecast state during prerendering and then retrieves the state to initialize the component. The [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) persists the component state after all component invocations.
In the following example, the `WeatherForecastPreserveState` component persists weather forecast state during prerendering and then retrieves the state to initialize the component. The [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) persists the component state after all component invocations.

`Pages/WeatherForecastPreserveState.razor`:

Expand Down Expand Up @@ -502,7 +579,7 @@ else
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
"fetchdata", out var restored))
nameof(forecasts), out var restored))
{
forecasts =
await WeatherForecastService.GetForecastAsync(
Expand All @@ -519,7 +596,7 @@ else

private Task PersistData()
{
ApplicationState.PersistAsJson("fetchdata", forecasts);
ApplicationState.PersistAsJson(nameof(forecasts), forecasts);

return Task.CompletedTask;
}
Expand All @@ -531,6 +608,10 @@ else
}
```

:::moniker-end

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

By initializing components with the same state used during prerendering, any expensive initialization steps are only executed once. The rendered UI also matches the prerendered UI, so no flicker occurs in the browser.

The persisted prerendered state is transferred to the client, where it's used to restore the component state. [ASP.NET Core Data Protection](xref:security/data-protection/introduction) ensures that the data is transferred securely in Blazor Server apps.
Expand Down Expand Up @@ -969,7 +1050,7 @@ To solve these problems, Blazor supports persisting state in a prerendered page

Decide what state to persist using the <xref:Microsoft.AspNetCore.Components.PersistentComponentState> service. <xref:Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnPersisting%2A?displayProperty=nameWithType> registers a callback to persist the component state before the app is paused. The state is retrieved when the application resumes. Make the call at the end of initialization code in order to avoid a potential race condition during app shutdown.

The following example is an updated version of the `FetchData` component based on the Blazor project template. The `WeatherForecastPreserveState` component persists weather forecast state during prerendering and then retrieves the state to initialize the component. The [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) persists the component state after all component invocations.
In the following example, the `WeatherForecastPreserveState` component persists weather forecast state during prerendering and then retrieves the state to initialize the component. The [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) persists the component state after all component invocations.

`Pages/WeatherForecastPreserveState.razor`:

Expand Down Expand Up @@ -1022,7 +1103,7 @@ else
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
"fetchdata", out var restored))
nameof(forecasts), out var restored))
{
forecasts =
await WeatherForecastService.GetForecastAsync(DateTime.Now);
Expand All @@ -1038,7 +1119,7 @@ else

private Task PersistData()
{
ApplicationState.PersistAsJson("fetchdata", forecasts);
ApplicationState.PersistAsJson(nameof(forecasts), forecasts);

return Task.CompletedTask;
}
Expand Down
48 changes: 45 additions & 3 deletions aspnetcore/blazor/components/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ Prerendering waits for *quiescence*, which means that a component doesn't render

Welcome to your new app.

<SlowComponent />
<Slow />
```

> [!NOTE]
Expand All @@ -616,6 +616,46 @@ When the `Home` component is prerendering, the `Slow` component is quickly rende

To address the double rendering of the loading message and the re-execution of service and database calls, persist prerendered state with <xref:Microsoft.AspNetCore.Components.PersistentComponentState> for final rendering of the component, as seen in the following updates to the `Slow` component:

:::moniker-end

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

```razor
@page "/slow"
@attribute [StreamRendering]

<h2>Slow Component</h2>

@if (Data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@Data</div>
}

@code {
[SupplyParameterFromPersistentComponentState]
public string? Data { get; set; }

protected override async Task OnInitializedAsync()
{
Data ??= await LoadDataAsync();
}

private async Task<string> LoadDataAsync()
{
await Task.Delay(10000);
return "Finished!";
}
}
```

:::moniker-end

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

```razor
@page "/slow"
@attribute [StreamRendering]
Expand All @@ -639,7 +679,7 @@ else

protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<string>("data", out var restored))
if (!ApplicationState.TryTakeFromJson<string>(nameof(data), out var restored))
{
data = await LoadDataAsync();
}
Expand All @@ -654,7 +694,7 @@ else

private Task PersistData()
{
ApplicationState.PersistAsJson("data", data);
ApplicationState.PersistAsJson(nameof(data), data);

return Task.CompletedTask;
}
Expand All @@ -672,6 +712,8 @@ else
}
```

:::moniker-end

By combining streaming rendering with persistent component state:

* Services and databases only require a single call for component initialization.
Expand Down
Loading
Loading