blazor - 在 Blazor 中,为什么调用 NavigationManager.NavigateTo 有时会导致使用旧值进行额外的 OnParametersSetAsync 调用?

标签 blazor blazor-webassembly

在 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 的调用是伪造的(或者它不代表对参数的真正更改)?

我尝试跟踪先前的值并将其与当前值进行比较,但这没有帮助,因为"new"值是在旧值之前处理的——换句话说,上面的 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}");
}

关于blazor - 在 Blazor 中,为什么调用 NavigationManager.NavigateTo 有时会导致使用旧值进行额外的 OnParametersSetAsync 调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63945954/

相关文章:

c# - 我可以使用 Swashbuckle 从 Blazor 项目 c# 生成 Swagger UI

asp.net-core - 如何使用绑定(bind)值和绑定(bind)值 :event on a custom component Blazor

azure - 如何解决 azure 发布的 Blazor 应用程序上的 XMLHttpRequest 错误

binding - blazor 中的绑定(bind)延迟了 1 步

blazor-webassembly - 如何将 blazorise RichTextEdit 组件绑定(bind)到模型属性

blazor - .Net Core Blazor - 使用输入复选框与 <tr> onclick 绑定(bind)时出现 Javascript 错误

c# - Razor 页面 (blazor) 正在对字符进行编码

blazor - 属性如何导致 Blazor 中的页面更新?

c# - HttpClient - 请求已取消 - 超时 100 秒

c# - 客户端不刷新新对象的ID | ASP.NET 核心 5