在Blazor中,为什么调用NavigationManager.NavigateTo(string)
有时会导致使用旧值额外调用OnParametersSetAsync调用?
我有一个页面,它通过CallbackEvent对点击做出反应,父级调用NavigationManager.NavigateTo
来设置一个新的URL,这会导致父级的Parameters更新,然后组件通过OnParameterSetAsync对新值做出响应但在此之前,还使用旧值调用了OnParametersSetAsync——显然是在它们更改之前。我的第二次调用似乎经常在第一次(糟糕的(调用之前完成,因此第一次调用最后完成,结果很糟糕。
那么,对OnParametersSetAsync
的第一次看似虚假和不正确的调用是否只是因为事件处理程序等待时属性可能发生了更改?我如何确定这是一个虚假的呼叫?
这里有一个触发它的例子:
@page "/demo"
@page "/demo/{SelectedOrderId:int}"
@using Microsoft.Extensions.Configuration
@inject IHttpClientFactory clientFactory
@inject IToastService toastService
@inject IConfiguration Configuration
@inject NavigationManager navigationManager
<div class="mt-4 container-fluid">
<div class="row">
<div class="col-12 col-md-8 order-md-2">
Order @OrderDetail?.OrderId @OrderDetail?.ProductCode
</div>
<div class="col-12 col-md-4 order-md-1">
@foreach (var order in Orders)
{
<button @onclick="async () => await OnSelectedOrderIdChanged(order.OrderId)">@order.OrderId</button> }
</div>
</div>
</div>
@code {
[CascadingParameter]
public AppState State { get; set; } = null!;
public int? CustomerId { get; set; } = 8010;
[Parameter]
public int? SelectedOrderId { get; set; } = null;
private List<Order> Orders { get; set; } = new List<Order>();
private OrderDetail? OrderDetail { get; set; } = null;
protected override async Task OnInitializedAsync()
{
await LoadOrdersList();
}
protected override async Task OnParametersSetAsync()
{
Console.WriteLine($"params changed to order {SelectedOrderId}");
await LoadOrderDetail();
Console.WriteLine($"done loading order {SelectedOrderId}");
}
private async Task LoadOrdersList()
{
string serviceEndpoint = Configuration["MyServiceUrl"];
string url = $"{serviceEndpoint}/orders?customerId={CustomerId}";
Orders = await clientFactory.CreateClient().GetFromJsonAsync<List<Order>>(url);
}
private async Task LoadOrderDetail()
{
string serviceEndpoint = Configuration["MyServiceUrl"];
string url = $"{serviceEndpoint}/orders/{SelectedOrderId}";
OrderDetail = await clientFactory.CreateClient().GetFromJsonAsync<OrderDetail>(url);
}
private async Task OnSelectedOrderIdChanged(int? newOrderId)
{
SelectedOrderId = newOrderId;
await Task.Yield(); // could be anything
navigationManager.NavigateTo($"/demo/{newOrderId}");
}
}
当我运行这个,然后点击订单48026528的一个按钮时,我进入控制台:
params changed to order 48026500
params changed to order 48026528
done loading order 48026528
页面中的显示顺序为48026500!据推测,应该提前进行UI更新。那么,我如何确定48026500的调用是伪造的(或者它不代表参数的真正更改(?
我尝试跟踪以前的值并将其与当前值进行比较,但这没有帮助,因为";新的";值在旧值之前被处理——换句话说,上面的LoadOrderDetail()
运行两次,并且它们竞争,通常带有新值的LoadOrderDetail首先运行,然后带有旧值的LoadOrderDetail完成并覆盖新数据。那么,如何避免对这些虚假事件调用LoadOrderDetail呢?
NavigationManager.NavigateTo(string)
仅调用一次。这是你的问题(你非常接近!(:
[Parameter]
public int? SelectedOrderId { get; set; } = null;
. . . . .
private async Task OnSelectedOrderIdChanged(int? newOrderId)
{
// Setting a [Parameter] prop might "work", but is not intended
SelectedOrderId = newOrderId;
await Task.Yield();
// This will also update SelectedOrderId
navigationManager.NavigateTo($"/demo/{newOrderId}");
}
请注意,[Parameter]
属性应由父级或通过@page
路由参数设置,而不是由组件本身设置。
navigationManager.NavigateTo($"/demo/{newOrderId}");
将设置SelectedOrderId
;这是更新页面[Parameter]
属性的正确方法因此,SelectedOrderId = newOrderId
不是必需的。删除该行,您的代码将按预期工作。
此外,您提到跟踪以前的值以确定何时更新。这可能很有用(尤其是与ShouldRender
配对时(。最简单的方法是跟踪以前的id,而不是以前的值。应用于您的代码:
[Parameter]
public int? SelectedOrderId { get; set; }
private int? PreviousOrderId { get; set; }
. . . . .
protected override async Task OnParametersSetAsync()
{
Console.WriteLine($"params changed to order {SelectedOrderId}");
if (PreviousOrderId != SelectedOrderId) {
// Prevent the load from ever happening if the id doesn't change.
await LoadOrderDetail();
}
PreviousOrderId = SelectedOrderId;
Console.WriteLine($"done loading order {SelectedOrderId}");
}