Skip to content

Commit f880349

Browse files
authored
Don't render route component if OnNavigateAsync task in-progress (#24225)
1 parent 5e49fc3 commit f880349

File tree

3 files changed

+59
-12
lines changed

3 files changed

+59
-12
lines changed

src/Components/Components/src/Routing/Router.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,19 @@ private void RefreshRouteTable()
152152

153153
internal virtual void Refresh(bool isNavigationIntercepted)
154154
{
155+
// If an `OnNavigateAsync` task is currently in progress, then wait
156+
// for it to complete before rendering. Note: because _previousOnNavigateTask
157+
// is initialized to a CompletedTask on initialization, this will still
158+
// allow first-render to complete successfully.
159+
if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion)
160+
{
161+
if (Navigating != null)
162+
{
163+
_renderHandle.Render(Navigating);
164+
}
165+
return;
166+
}
167+
155168
RefreshRouteTable();
156169

157170
var locationPath = NavigationManager.ToBaseRelativePath(_locationAbsolute);
@@ -248,19 +261,15 @@ internal async Task RunOnNavigateWithRefreshAsync(string path, bool isNavigation
248261
var previousTask = _previousOnNavigateTask;
249262
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
250263
_previousOnNavigateTask = tcs.Task;
251-
try
252-
{
253-
// And pass an indicator for the previous task to the currently running one.
254-
var shouldRefresh = await RunOnNavigateAsync(path, previousTask);
255-
if (shouldRefresh)
256-
{
257-
Refresh(isNavigationIntercepted);
258-
}
259-
}
260-
finally
264+
265+
// And pass an indicator for the previous task to the currently running one.
266+
var shouldRefresh = await RunOnNavigateAsync(path, previousTask);
267+
tcs.SetResult();
268+
if (shouldRefresh)
261269
{
262-
tcs.SetResult();
270+
Refresh(isNavigationIntercepted);
263271
}
272+
264273
}
265274

266275
private void OnLocationChanged(object sender, LocationChangedEventArgs args)

src/Components/test/E2ETest/Tests/RoutingTest.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,29 @@ public void OnNavigate_CanRenderUIForExceptions()
578578
Assert.NotNull(errorUiElem);
579579
}
580580

581+
[Fact]
582+
public void OnNavigate_DoesNotRenderWhileOnNavigateExecuting()
583+
{
584+
var app = Browser.MountTestComponent<TestRouterWithOnNavigate>();
585+
586+
// Navigate to a route
587+
SetUrlViaPushState("/WithParameters/name/Abc");
588+
589+
// Click the button to trigger a re-render
590+
var button = app.FindElement(By.Id("trigger-rerender"));
591+
button.Click();
592+
593+
// Assert that the parameter route didn't render
594+
Browser.DoesNotExist(By.Id("test-info"));
595+
596+
// Navigate to another page to cancel the previous `OnNavigateAsync`
597+
// task and trigger a re-render on its completion
598+
SetUrlViaPushState("/LongPage1");
599+
600+
// Confirm that the route was rendered
601+
Browser.Equal("This is a long page you can scroll.", () => app.FindElement(By.Id("test-info")).Text);
602+
}
603+
581604
private long BrowserScrollY
582605
{
583606
get => (long)((IJavaScriptExecutor)Browser).ExecuteScript("return window.scrollY");

src/Components/test/testassets/BasicTestApp/RouterTest/TestRouterWithOnNavigate.razor

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
@using Microsoft.AspNetCore.Components.Routing
22

3+
@using System.Threading
4+
5+
<button @onclick="TriggerRerender" id="trigger-rerender">Trigger Rerender</button>
6+
37
<Router AppAssembly="@typeof(BasicTestApp.Program).Assembly" OnNavigateAsync="@OnNavigateAsync">
48
<Navigating>
59
<div style="padding: 20px;background-color:blue;color:white;" id="loading-banner">
@@ -21,7 +25,8 @@
2125
{
2226
{ "LongPage1", new Func<NavigationContext, Task>(TestLoadingPageShows) },
2327
{ "LongPage2", new Func<NavigationContext, Task>(TestOnNavCancel) },
24-
{ "Other", new Func<NavigationContext, Task>(TestOnNavException) }
28+
{ "Other", new Func<NavigationContext, Task>(TestOnNavException) },
29+
{"WithParameters/name/Abc", new Func<NavigationContext, Task>(TestRefreshHandling)}
2530
};
2631

2732
private async Task OnNavigateAsync(NavigationContext args)
@@ -50,4 +55,14 @@
5055
await Task.CompletedTask;
5156
throw new Exception("This is an uncaught exception.");
5257
}
58+
59+
public static async Task TestRefreshHandling(NavigationContext args)
60+
{
61+
await Task.Delay(Timeout.Infinite, args.CancellationToken);
62+
}
63+
64+
private void TriggerRerender()
65+
{
66+
Console.WriteLine("Nothing to see here, just an even to trigger a re-render...");
67+
}
5368
}

0 commit comments

Comments
 (0)