Skip to content

Commit fe1d427

Browse files
committed
Fix keyless enhanced nav updates
1 parent 7e839c0 commit fe1d427

File tree

5 files changed

+80
-62
lines changed

5 files changed

+80
-62
lines changed

src/Components/Samples/BlazorUnitedApp/Pages/FetchData.razor

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,14 @@
22
@using BlazorUnitedApp.Data
33
@inject WeatherForecastService ForecastService
44

5-
<FetchDataChildren @key="0" @rendermode="InteractiveServer"></FetchDataChildren>
6-
7-
@* <PageTitle>Weather forecast</PageTitle>
8-
9-
<h1>Weather forecast</h1>
10-
11-
<p>This component demonstrates fetching data from a service.</p>
12-
13-
@if (Forecasts is null)
14-
{
15-
<p><em>Loading...</em></p>
16-
}
17-
else
18-
{
19-
<table class="table">
20-
<thead>
21-
<tr>
22-
<th>Date</th>
23-
<th>Temp. (C)</th>
24-
<th>Temp. (F)</th>
25-
<th>Summary</th>
26-
</tr>
27-
</thead>
28-
<tbody>
29-
@foreach (var forecast in Forecasts)
30-
{
31-
<tr>
32-
<td>@forecast.Date.ToShortDateString()</td>
33-
<td>@forecast.TemperatureC</td>
34-
<td>@forecast.TemperatureF</td>
35-
<td>@forecast.Summary</td>
36-
</tr>
37-
}
38-
</tbody>
39-
</table>
40-
}
41-
5+
<FetchDataChildren @rendermode="InteractiveServer" YayOrNay="@YayOrNay"></FetchDataChildren>
426
@code {
43-
private WeatherForecast[]? Forecasts { get; set; }
7+
[CascadingParameter] public HttpContext Context { get; set; } = null!;
448

45-
protected override async Task OnInitializedAsync()
9+
protected override void OnInitialized()
4610
{
47-
Forecasts ??= await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
11+
YayOrNay = Context.Request.Query["YayOrNay"] == "true" ? true : false;
4812
}
13+
14+
public bool YayOrNay { get; set; } = false;
4915
}
50-
*@

src/Components/Samples/BlazorUnitedApp/Pages/FetchDataChildren.razor

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@using BlazorUnitedApp.Data
22
@inject WeatherForecastService ForecastService
33
@inject NavigationManager NavigationManager
4-
@attribute [StreamRendering]
54

65
<h1>Weather forecast</h1>
76

@@ -47,7 +46,7 @@ else
4746
<a href="fetchdata/?page=0">Page 0</a>
4847
</li>
4948
<li>
50-
<a href="fetchdata/?page=1">Page 1</a>
49+
<a href="fetchdata/?page=1&YayOrNay=true">Page 1</a>
5150
</li>
5251
<li>
5352
<a href="fetchdata/?page=2">Page 2</a>

src/Components/Server/src/Circuits/CircuitHost.cs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ public async Task OnConnectionUpAsync(CancellationToken cancellationToken)
311311

312312
public async Task OnConnectionDownAsync(CancellationToken cancellationToken)
313313
{
314-
if(_onConnectionDownFired)
314+
if (_onConnectionDownFired)
315315
{
316316
return;
317317
}
@@ -772,6 +772,8 @@ internal Task UpdateRootComponents(
772772
var shouldWaitForQuiescence = false;
773773
var operations = operationBatch.Operations;
774774
var batchId = operationBatch.BatchId;
775+
var postRemovalTask = Task.CompletedTask;
776+
TaskCompletionSource? taskCompletionSource = null;
775777
try
776778
{
777779
if (Descriptors.Count > 0)
@@ -787,21 +789,29 @@ internal Task UpdateRootComponents(
787789
shouldClearStore = true;
788790
// We only do this if we have no root components. Otherwise, the state would have been
789791
// provided during the start up process
790-
var appLifetime = _scope.ServiceProvider.GetRequiredService<ComponentStatePersistenceManager>();
792+
var persistenceManager = _scope.ServiceProvider.GetRequiredService<ComponentStatePersistenceManager>();
791793
if (_isFirstUpdate)
792794
{
793-
appLifetime.SetPlatformRenderMode(RenderMode.InteractiveServer);
795+
persistenceManager.SetPlatformRenderMode(RenderMode.InteractiveServer);
794796
}
795797

796798
// Use the appropriate scenario based on whether this is a restore operation
797-
var scenario = (isRestore, _isFirstUpdate) switch
799+
var context = (isRestore, _isFirstUpdate) switch
798800
{
799801
(_, false) => RestoreContext.ValueUpdate,
800802
(true, _) => RestoreContext.LastSnapshot,
801803
(false, _) => RestoreContext.InitialValue
802804
};
803-
804-
await appLifetime.RestoreStateAsync(store, scenario);
805+
if (context == RestoreContext.ValueUpdate)
806+
{
807+
taskCompletionSource = new();
808+
postRemovalTask = EnqueueRestore(taskCompletionSource, persistenceManager, store);
809+
}
810+
else
811+
{
812+
// Trigger the restore of the state right away.
813+
await persistenceManager.RestoreStateAsync(store, context);
814+
}
805815
}
806816

807817
if (_isFirstUpdate)
@@ -825,7 +835,10 @@ internal Task UpdateRootComponents(
825835
}
826836
}
827837

828-
await PerformRootComponentOperations(operations, shouldWaitForQuiescence);
838+
var operationsTask = PerformRootComponentOperations(operations, shouldWaitForQuiescence, postRemovalTask);
839+
taskCompletionSource?.SetResult();
840+
841+
await operationsTask;
829842

830843
await Client.SendAsync("JS.EndUpdateRootComponents", batchId);
831844

@@ -851,11 +864,23 @@ internal Task UpdateRootComponents(
851864
});
852865
}
853866

867+
private static async Task EnqueueRestore(
868+
TaskCompletionSource taskCompletionSource,
869+
ComponentStatePersistenceManager manager,
870+
IPersistentComponentStateStore store)
871+
{
872+
await taskCompletionSource.Task;
873+
await manager.RestoreStateAsync(store);
874+
}
875+
854876
private async ValueTask PerformRootComponentOperations(
855877
RootComponentOperation[] operations,
856-
bool shouldWaitForQuiescence)
878+
bool shouldWaitForQuiescence,
879+
Task postStateTask)
857880
{
858881
var webRootComponentManager = Renderer.GetOrCreateWebRootComponentManager();
882+
webRootComponentManager.SetCurrentUpdateTask(postStateTask);
883+
859884
var pendingTasks = shouldWaitForQuiescence
860885
? new Task[operations.Length]
861886
: null;
@@ -875,6 +900,7 @@ await HandleInboundActivityAsync(() =>
875900
operation.Descriptor.ComponentType,
876901
operation.Marker.Value.Key,
877902
operation.Descriptor.Parameters);
903+
878904
pendingTasks?[i] = task;
879905
break;
880906
case RootComponentOperationType.Update:

src/Components/Shared/src/WebRootComponentManager.cs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public WebRootComponentManager GetOrCreateWebRootComponentManager()
3131
public sealed class WebRootComponentManager(Renderer renderer)
3232
{
3333
private readonly Dictionary<int, WebRootComponent> _webRootComponents = new();
34+
private Task _currentUpdateTask = Task.CompletedTask;
3435

3536
public async Task AddRootComponentAsync(
3637
int ssrComponentId,
@@ -52,7 +53,7 @@ public async Task AddRootComponentAsync(
5253

5354
var component = WebRootComponent.Create(renderer, componentType, ssrComponentId, key, parameters);
5455
_webRootComponents.Add(ssrComponentId, component);
55-
56+
await _currentUpdateTask;
5657
await component.RenderAsync(renderer);
5758
}
5859

@@ -63,7 +64,7 @@ public Task UpdateRootComponentAsync(
6364
WebRootComponentParameters newParameters)
6465
{
6566
var component = GetRequiredWebRootComponent(ssrComponentId);
66-
return component.UpdateAsync(renderer, newComponentType, newKey, newParameters);
67+
return component.UpdateAsync(renderer, newComponentType, newKey, newParameters, _currentUpdateTask);
6768
}
6869

6970
public void RemoveRootComponent(int ssrComponentId)
@@ -97,7 +98,7 @@ internal ComponentMarkerKey GetRootComponentKey(int componentId)
9798
{
9899
foreach (var (_, candidate) in _webRootComponents)
99100
{
100-
var(id, key, _, _) = candidate;
101+
var (id, key, _, _) = candidate;
101102
if (id == componentId)
102103
{
103104
return key;
@@ -107,6 +108,11 @@ internal ComponentMarkerKey GetRootComponentKey(int componentId)
107108
return default;
108109
}
109110

111+
internal void SetCurrentUpdateTask(Task postStateTask)
112+
{
113+
_currentUpdateTask = postStateTask;
114+
}
115+
110116
private sealed class WebRootComponent
111117
{
112118
[DynamicallyAccessedMembers(Component)]
@@ -165,7 +171,8 @@ public Task UpdateAsync(
165171
Renderer renderer,
166172
[DynamicallyAccessedMembers(Component)] Type newComponentType,
167173
ComponentMarkerKey? newKey,
168-
WebRootComponentParameters newParameters)
174+
WebRootComponentParameters newParameters,
175+
Task currentUpdateTask)
169176
{
170177
if (_componentType != newComponentType)
171178
{
@@ -189,7 +196,7 @@ public Task UpdateAsync(
189196
// We can supply new parameters if the key has a @key value, because that means the client
190197
// opted in to dynamic parameter updates.
191198
_latestParameters = newParameters;
192-
return RenderAsync(renderer);
199+
return RenderAsync(renderer, currentUpdateTask);
193200
}
194201
else
195202
{
@@ -207,13 +214,22 @@ public Task UpdateAsync(
207214
renderer.RemoveRootComponent(_interactiveComponentId);
208215
_interactiveComponentId = renderer.AddRootComponent(_componentType, _ssrComponentIdString);
209216
_latestParameters = newParameters;
210-
return RenderAsync(renderer);
217+
return RenderAsync(renderer, currentUpdateTask);
211218
}
212219
}
213220
}
214221

215222
public Task RenderAsync(Renderer renderer)
216-
=> renderer.RenderRootComponentAsync(_interactiveComponentId, _latestParameters.Parameters);
223+
{
224+
return renderer.RenderRootComponentAsync(_interactiveComponentId, _latestParameters.Parameters);
225+
}
226+
227+
public async Task RenderAsync(Renderer renderer, Task updateTask)
228+
{
229+
await updateTask;
230+
await renderer.RenderRootComponentAsync(_interactiveComponentId, _latestParameters.Parameters);
231+
}
232+
217233

218234
public void Remove(Renderer renderer)
219235
{

src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,17 @@ public WebAssemblyRenderer(IServiceProvider serviceProvider, ResourceAssetCollec
5252
private void OnUpdateRootComponents(RootComponentOperationBatch batch, string appState)
5353
{
5454
var webRootComponentManager = GetOrCreateWebRootComponentManager();
55-
55+
TaskCompletionSource? taskCompletionSource = null;
56+
var stateUpdateTask = Task.CompletedTask;
5657
var store = !string.IsNullOrEmpty(appState) ? new PrerenderComponentApplicationStore(appState) : null;
5758
if (store != null)
5859
{
59-
// Restore the state from the store if it exists
60-
_ = _componentStatePersistenceManager.RestoreStateAsync(store, RestoreContext.ValueUpdate);
60+
taskCompletionSource = new TaskCompletionSource();
61+
stateUpdateTask = EnqueueRestore(taskCompletionSource.Task, _componentStatePersistenceManager, store);
6162
}
6263

64+
webRootComponentManager.SetCurrentUpdateTask(stateUpdateTask);
65+
6366
for (var i = 0; i < batch.Operations.Length; i++)
6467
{
6568
var operation = batch.Operations[i];
@@ -84,12 +87,21 @@ private void OnUpdateRootComponents(RootComponentOperationBatch batch, string ap
8487
break;
8588
}
8689
}
87-
90+
taskCompletionSource?.SetResult();
8891
store?.ExistingState.Clear();
8992

9093
NotifyEndUpdateRootComponents(batch.BatchId);
9194
}
9295

96+
private static async Task EnqueueRestore(
97+
Task task,
98+
ComponentStatePersistenceManager componentStatePersistenceManager,
99+
PrerenderComponentApplicationStore store)
100+
{
101+
await task;
102+
await componentStatePersistenceManager.RestoreStateAsync(store, RestoreContext.ValueUpdate);
103+
}
104+
93105
protected override IComponentRenderMode? GetComponentRenderMode(IComponent component) => RenderMode.InteractiveWebAssembly;
94106

95107
public void NotifyEndUpdateRootComponents(long batchId)

0 commit comments

Comments
 (0)