在 Blazor 中,为什么调用 NavigationManager.NavigateTo 有时会导致使用旧值进行额外的 OnParametersSetAsync 调用?
In Blazor, why does invoking NavigationManager.NavigateTo sometimes result in an extra OnParametersSetAsync call with old values?
在 Blazor 中,为什么调用 NavigationManager.NavigateTo(string)
有时会导致使用旧值额外调用 OnParametersSetAsync 调用?
我有一个页面通过 CallbackEvent 响应点击,父级调用 NavigationManager.NavigateTo
设置新的 URL,这会导致父级的参数更新,然后组件通过 OnParametersSetAsync 响应新值。 但在此之前,还使用旧值调用了 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 的调用是伪造的(或者它不代表对参数的真正更改)?
我尝试跟踪以前的值并将其与当前值进行比较,但这没有帮助,因为“新”值在之前处理旧值--in换句话说,LoadOrderDetail()
超过 运行s 两次,他们竞争,并且通常首先使用新值 运行s 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}");
}
在 Blazor 中,为什么调用 NavigationManager.NavigateTo(string)
有时会导致使用旧值额外调用 OnParametersSetAsync 调用?
我有一个页面通过 CallbackEvent 响应点击,父级调用 NavigationManager.NavigateTo
设置新的 URL,这会导致父级的参数更新,然后组件通过 OnParametersSetAsync 响应新值。 但在此之前,还使用旧值调用了 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 的调用是伪造的(或者它不代表对参数的真正更改)?
我尝试跟踪以前的值并将其与当前值进行比较,但这没有帮助,因为“新”值在之前处理旧值--in换句话说,LoadOrderDetail()
超过 运行s 两次,他们竞争,并且通常首先使用新值 运行s 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}");
}