Skip to content

Commit f0386af

Browse files
authored
Declarative persistent component state updates (#35210)
1 parent 9d68074 commit f0386af

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

Diff for: aspnetcore/blazor/components/integration.md

+5
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,11 @@ In the following example, the `{TYPE}` placeholder represents the type of data t
416416
@code {
417417
[SupplyParameterFromPersistentComponentState]
418418
public {TYPE} Data { get; set; }
419+
420+
protected override async Task OnInitializedAsync()
421+
{
422+
Data ??= await ...;
423+
}
419424
}
420425
```
421426

Diff for: aspnetcore/blazor/components/lifecycle.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ else
646646
647647
private async Task<string> LoadDataAsync()
648648
{
649-
await Task.Delay(10000);
649+
await Task.Delay(5000);
650650
return "Finished!";
651651
}
652652
}
@@ -701,7 +701,7 @@ else
701701
702702
private async Task<string> LoadDataAsync()
703703
{
704-
await Task.Delay(10000);
704+
await Task.Delay(5000);
705705
return "Finished!";
706706
}
707707

Diff for: aspnetcore/blazor/components/prerender.md

+82-5
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,55 @@ The following counter component example persists counter state during prerenderi
7979
8080
<h1>Prerendered Counter 2</h1>
8181
82+
<p role="status">Current count: @State?.CurrentCount</p>
83+
84+
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
85+
86+
@code {
87+
[SupplyParameterFromPersistentComponentState]
88+
public CounterState? State { get; set; }
89+
90+
protected override void OnInitialized()
91+
{
92+
if (State is null)
93+
{
94+
State = new() { CurrentCount = Random.Shared.Next(100) };
95+
Logger.LogInformation("CurrentCount set to {Count}",
96+
State.CurrentCount);
97+
}
98+
else
99+
{
100+
Logger.LogInformation("CurrentCount restored to {Count}",
101+
State.CurrentCount);
102+
}
103+
}
104+
105+
private void IncrementCount()
106+
{
107+
if (State is not null)
108+
{
109+
State.CurrentCount++;
110+
}
111+
}
112+
113+
public class CounterState
114+
{
115+
public int CurrentCount { get; set; }
116+
}
117+
}
118+
```
119+
120+
<!-- HOLD until https://github.com/dotnet/aspnetcore/issues/61456
121+
is resolved
122+
123+
```razor
124+
@page "/prerendered-counter-2"
125+
@inject ILogger<PrerenderedCounter2> Logger
126+
127+
<PageTitle>Prerendered Counter 2</PageTitle>
128+
129+
<h1>Prerendered Counter 2</h1>
130+
82131
<p role="status">Current count: @CurrentCount</p>
83132
84133
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@@ -89,13 +138,31 @@ The following counter component example persists counter state during prerenderi
89138
90139
protected override void OnInitialized()
91140
{
92-
CurrentCount ??= Random.Shared.Next(100);
93-
Logger.LogInformation("CurrentCount set to {Count}", CurrentCount);
141+
if (CurrentCount == 0)
142+
{
143+
CurrentCount = Random.Shared.Next(100);
144+
Logger.LogInformation("CurrentCount set to {Count}", CurrentCount);
145+
}
146+
else
147+
{
148+
Logger.LogInformation("CurrentCount restored to {Count}", CurrentCount);
149+
}
94150
}
95151
96152
private void IncrementCount() => CurrentCount++;
97153
}
98154
```
155+
-->
156+
157+
When the component executes, `CurrentCount` is only set once during prerendering. The value is restored when the component is rerendered. The following is example output.
158+
159+
> [!NOTE]
160+
> If the app adopts [interactive routing](xref:blazor/fundamentals/routing#static-versus-interactive-routing) and the page is reached via an internal [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling), prerendering doesn't occur. Therefore, you must perform a full page reload for the component to see the following output. For more information, see the [Interactive routing and prerendering](#interactive-routing-and-prerendering) section.
161+
162+
> :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter2[0]":::
163+
> :::no-loc text=" CurrentCount set to 96":::
164+
> :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter2[0]":::
165+
> :::no-loc text=" CurrentCount restored to 96":::
99166
100167
In the following example that serializes state for multiple components of the same type:
101168

@@ -283,6 +350,16 @@ The following counter component example persists counter state during prerenderi
283350
}
284351
```
285352

353+
When the component executes, `currentCount` is only set once during prerendering. The value is restored when the component is rerendered. The following is example output.
354+
355+
> [!NOTE]
356+
> If the app adopts [interactive routing](xref:blazor/fundamentals/routing#static-versus-interactive-routing) and the page is reached via an internal [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling), prerendering doesn't occur. Therefore, you must perform a full page reload for the component to see the following output. For more information, see the [Interactive routing and prerendering](#interactive-routing-and-prerendering) section.
357+
358+
> :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter3[0]":::
359+
> :::no-loc text=" currentCount set to 96":::
360+
> :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter3[0]":::
361+
> :::no-loc text=" currentCount restored to 96":::
362+
286363
:::moniker-end
287364

288365
:::moniker range="< aspnetcore-10.0"
@@ -340,18 +417,18 @@ The following counter component example persists counter state during prerenderi
340417

341418
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/PrerenderedCounter2.razor":::
342419

343-
:::moniker-end
344-
345420
When the component executes, `currentCount` is only set once during prerendering. The value is restored when the component is rerendered. The following is example output.
346421

347422
> [!NOTE]
348-
> If the app adopts [interactive routing](xref:blazor/fundamentals/routing#static-versus-interactive-routing) and the page is reached via an internal [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling), prerendering doesn't occur. Therefore, you must perform a full page reload for the `PrerenderedCounter2` component to see the following output. For more information, see the [Interactive routing and prerendering](#interactive-routing-and-prerendering) section.
423+
> If the app adopts [interactive routing](xref:blazor/fundamentals/routing#static-versus-interactive-routing) and the page is reached via an internal [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling), prerendering doesn't occur. Therefore, you must perform a full page reload for the component to see the following output. For more information, see the [Interactive routing and prerendering](#interactive-routing-and-prerendering) section.
349424
350425
> :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter2[0]":::
351426
> :::no-loc text=" currentCount set to 96":::
352427
> :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter2[0]":::
353428
> :::no-loc text=" currentCount restored to 96":::
354429
430+
:::moniker-end
431+
355432
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.
356433

357434
The persisted prerendered state is transferred to the client, where it's used to restore the component state. During client-side rendering (CSR, `InteractiveWebAssembly`), the data is exposed to the browser and must not contain sensitive, private information. During interactive server-side rendering (interactive SSR, `InteractiveServer`), [ASP.NET Core Data Protection](xref:security/data-protection/introduction) ensures that the data is transferred securely. The `InteractiveAuto` render mode combines WebAssembly and Server interactivity, so it's necessary to consider data exposure to the browser, as in the CSR case.

0 commit comments

Comments
 (0)