diff --git a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor
index 9024a800a71b..5f1f5a6c1747 100644
--- a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor
+++ b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor
@@ -48,10 +48,9 @@ else
WeatherForecast[] forecasts;
- public override Task SetParametersAsync(ParameterCollection parameters)
+ protected override void OnParametersSetting()
{
StartDate = DateTime.Now;
- return base.SetParametersAsync(parameters);
}
protected override async Task OnParametersSetAsync()
diff --git a/src/Components/Components/src/ComponentBase.cs b/src/Components/Components/src/ComponentBase.cs
index 045216b722c0..6c9a9dc4121b 100644
--- a/src/Components/Components/src/ComponentBase.cs
+++ b/src/Components/Components/src/ComponentBase.cs
@@ -78,7 +78,22 @@ protected virtual Task OnInitializedAsync()
///
/// Method invoked when the component has received parameters from its parent in
- /// the render tree, and the incoming values have been assigned to properties.
+ /// the render tree, before the incoming values have been assigned to properties.
+ ///
+ protected virtual void OnParametersSetting()
+ {
+ }
+
+ ///
+ /// Method invoked when the component has received parameters from its parent in
+ /// the render tree, before the incoming values have been assigned to properties.
+ ///
+ /// A representing any asynchronous operation.
+ protected virtual Task OnParametersSettingAsync() => Task.CompletedTask;
+
+ ///
+ /// Method invoked when the component has received parameters from its parent in
+ /// the render tree, after the incoming values have been assigned to properties.
///
protected virtual void OnParametersSet()
{
@@ -86,7 +101,7 @@ protected virtual void OnParametersSet()
///
/// Method invoked when the component has received parameters from its parent in
- /// the render tree, and the incoming values have been assigned to properties.
+ /// the render tree, after the incoming values have been assigned to properties.
///
/// A representing any asynchronous operation.
protected virtual Task OnParametersSetAsync()
@@ -175,22 +190,22 @@ void IComponent.Configure(RenderHandle renderHandle)
/// Method invoked to apply initial or updated parameters to the component.
///
/// The parameters to apply.
- public virtual Task SetParametersAsync(ParameterCollection parameters)
+ Task IComponent.SetParametersAsync(ParameterCollection parameters)
{
- parameters.SetParameterProperties(this);
if (!_initialized)
{
_initialized = true;
- return RunInitAndSetParametersAsync();
+ return RunInitAndSetParametersAsync(parameters);
}
else
{
- return CallOnParametersSetAsync();
+
+ return SetParametersCoreAsync(parameters);
}
}
- private async Task RunInitAndSetParametersAsync()
+ private async Task RunInitAndSetParametersAsync(ParameterCollection parameters)
{
OnInitialized();
var task = OnInitializedAsync();
@@ -223,9 +238,35 @@ private async Task RunInitAndSetParametersAsync()
// Don't call StateHasChanged here. CallOnParametersSetAsync should handle that for us.
}
+ await SetParametersCoreAsync(parameters);
+ }
+
+ private async Task SetParametersCoreAsync(ParameterCollection parameters)
+ {
+ await CallOnParametersSettingAsync();
+ parameters.SetParameterProperties(this);
await CallOnParametersSetAsync();
}
+ private Task CallOnParametersSettingAsync()
+ {
+ OnParametersSetting();
+ var task = OnParametersSettingAsync();
+ // If no async work is to be performed, i.e. the task has already ran to completion
+ // or was canceled by the time we got to inspect it, avoid going async and re-invoking
+ // StateHasChanged at the culmination of the async work.
+ var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion &&
+ task.Status != TaskStatus.Canceled;
+
+ // We always call StateHasChanged here as we want to trigger a rerender after OnParametersSet and
+ // the synchronous part of OnParametersSetAsync has run.
+ StateHasChanged();
+
+ return shouldAwaitTask ?
+ CallStateHasChangedOnAsyncCompletion(task) :
+ Task.CompletedTask;
+ }
+
private Task CallOnParametersSetAsync()
{
OnParametersSet();
diff --git a/src/Components/Components/src/Forms/InputComponents/InputBase.cs b/src/Components/Components/src/Forms/InputComponents/InputBase.cs
index 518876717fd5..849f8796a110 100644
--- a/src/Components/Components/src/Forms/InputComponents/InputBase.cs
+++ b/src/Components/Components/src/Forms/InputComponents/InputBase.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
-using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Components.Forms
{
@@ -166,12 +165,9 @@ protected string CssClass
}
}
-
///
- public override Task SetParametersAsync(ParameterCollection parameters)
+ protected override void OnParametersSet()
{
- parameters.SetParameterProperties(this);
-
if (EditContext == null)
{
// This is the first run
@@ -204,9 +200,6 @@ public override Task SetParametersAsync(ParameterCollection parameters)
throw new InvalidOperationException($"{GetType()} does not support changing the " +
$"{nameof(Forms.EditContext)} dynamically.");
}
-
- // For derived components, retain the usual lifecycle with OnInit/OnParametersSet/etc.
- return base.SetParametersAsync(ParameterCollection.Empty);
}
}
}
diff --git a/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor b/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor
index e13b253ff1bb..64e8bb8ba6f2 100644
--- a/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor
+++ b/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor
@@ -1,5 +1,6 @@
@page "/WithParameters/Name/{firstName}"
@page "/WithParameters/Name/{firstName}/LastName/{lastName}"
+@implements IComponent
Your full name is @FirstName @LastName.
@@ -9,11 +10,10 @@
[Parameter] string LastName { get ; set; }
- public override Task SetParametersAsync(ParameterCollection parameters)
+ protected override void OnParametersSetting()
{
// Manually reset parameters to defaults so we don't retain any from an earlier URL
FirstName = default;
LastName = default;
- return base.SetParametersAsync(parameters);
}
}