Don't render route component if OnNavigateAsync task in-progress (#24225)
This commit is contained in:
parent
5e49fc336e
commit
f88034902a
|
|
@ -152,6 +152,19 @@ namespace Microsoft.AspNetCore.Components.Routing
|
|||
|
||||
internal virtual void Refresh(bool isNavigationIntercepted)
|
||||
{
|
||||
// If an `OnNavigateAsync` task is currently in progress, then wait
|
||||
// for it to complete before rendering. Note: because _previousOnNavigateTask
|
||||
// is initialized to a CompletedTask on initialization, this will still
|
||||
// allow first-render to complete successfully.
|
||||
if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
if (Navigating != null)
|
||||
{
|
||||
_renderHandle.Render(Navigating);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshRouteTable();
|
||||
|
||||
var locationPath = NavigationManager.ToBaseRelativePath(_locationAbsolute);
|
||||
|
|
@ -248,19 +261,15 @@ namespace Microsoft.AspNetCore.Components.Routing
|
|||
var previousTask = _previousOnNavigateTask;
|
||||
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
_previousOnNavigateTask = tcs.Task;
|
||||
try
|
||||
|
||||
// And pass an indicator for the previous task to the currently running one.
|
||||
var shouldRefresh = await RunOnNavigateAsync(path, previousTask);
|
||||
tcs.SetResult();
|
||||
if (shouldRefresh)
|
||||
{
|
||||
// And pass an indicator for the previous task to the currently running one.
|
||||
var shouldRefresh = await RunOnNavigateAsync(path, previousTask);
|
||||
if (shouldRefresh)
|
||||
{
|
||||
Refresh(isNavigationIntercepted);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
tcs.SetResult();
|
||||
Refresh(isNavigationIntercepted);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnLocationChanged(object sender, LocationChangedEventArgs args)
|
||||
|
|
|
|||
|
|
@ -578,6 +578,29 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|||
Assert.NotNull(errorUiElem);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnNavigate_DoesNotRenderWhileOnNavigateExecuting()
|
||||
{
|
||||
var app = Browser.MountTestComponent<TestRouterWithOnNavigate>();
|
||||
|
||||
// Navigate to a route
|
||||
SetUrlViaPushState("/WithParameters/name/Abc");
|
||||
|
||||
// Click the button to trigger a re-render
|
||||
var button = app.FindElement(By.Id("trigger-rerender"));
|
||||
button.Click();
|
||||
|
||||
// Assert that the parameter route didn't render
|
||||
Browser.DoesNotExist(By.Id("test-info"));
|
||||
|
||||
// Navigate to another page to cancel the previous `OnNavigateAsync`
|
||||
// task and trigger a re-render on its completion
|
||||
SetUrlViaPushState("/LongPage1");
|
||||
|
||||
// Confirm that the route was rendered
|
||||
Browser.Equal("This is a long page you can scroll.", () => app.FindElement(By.Id("test-info")).Text);
|
||||
}
|
||||
|
||||
private long BrowserScrollY
|
||||
{
|
||||
get => (long)((IJavaScriptExecutor)Browser).ExecuteScript("return window.scrollY");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
@using Microsoft.AspNetCore.Components.Routing
|
||||
|
||||
@using System.Threading
|
||||
|
||||
<button @onclick="TriggerRerender" id="trigger-rerender">Trigger Rerender</button>
|
||||
|
||||
<Router AppAssembly="@typeof(BasicTestApp.Program).Assembly" OnNavigateAsync="@OnNavigateAsync">
|
||||
<Navigating>
|
||||
<div style="padding: 20px;background-color:blue;color:white;" id="loading-banner">
|
||||
|
|
@ -21,7 +25,8 @@
|
|||
{
|
||||
{ "LongPage1", new Func<NavigationContext, Task>(TestLoadingPageShows) },
|
||||
{ "LongPage2", new Func<NavigationContext, Task>(TestOnNavCancel) },
|
||||
{ "Other", new Func<NavigationContext, Task>(TestOnNavException) }
|
||||
{ "Other", new Func<NavigationContext, Task>(TestOnNavException) },
|
||||
{"WithParameters/name/Abc", new Func<NavigationContext, Task>(TestRefreshHandling)}
|
||||
};
|
||||
|
||||
private async Task OnNavigateAsync(NavigationContext args)
|
||||
|
|
@ -50,4 +55,14 @@
|
|||
await Task.CompletedTask;
|
||||
throw new Exception("This is an uncaught exception.");
|
||||
}
|
||||
|
||||
public static async Task TestRefreshHandling(NavigationContext args)
|
||||
{
|
||||
await Task.Delay(Timeout.Infinite, args.CancellationToken);
|
||||
}
|
||||
|
||||
private void TriggerRerender()
|
||||
{
|
||||
Console.WriteLine("Nothing to see here, just an even to trigger a re-render...");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue