在不相关的组件上调用 OnSetParameterAsync

ugh*_*ffs 0 c# blazor blazor-client-side blazor-webassembly

我目前正在开发一个使用 Blazor WebAssembly 作为前端框架的项目,但是,我认为我在状态管理方面遇到了一些复杂情况。这个问题发生在一个大型复杂的解决方案中,因此我重新创建并简化了它,这样我就可以在这里简洁地描述它,而不会显得臃肿。

我有一个包含两个组件的页面:朋友列表和关注者列表,每个组件都有一个按钮来切换按升序或降序对列表进行排序。

如果我重写并在FollowerList组件内部的OnParametersSetAsync上放置一个断点,然后单击FriendList组件的排序按钮,它会命中我在FollowerList组件上设置的断点。

我的印象是,只有当提供给该组件的属性发生更改或其状态发生更改时,才会在该组件上调用OnParametersSet/Async。在下面的代码中,您可以看到我有两个单独的列表,一个被馈送到一个组件,一个被馈送到另一个组件,所以我不确定如何在未触及的组件上触发此方法......

因此我的问题是:为什么它会在不相关的组件上遇到断点?这是故意行为吗?

页面预览

索引页,包含两个列表

索引剃刀

@page "/"
<button value="Sort Friends" @onclick=SortFriends>Sort Friends</button>
<FriendList Friends=SortedFriends />

<button value="Sort Followers" @onclick=SortFollowers>Sort Followers</button>
<FollowerList Followers=SortedFollowers />
Run Code Online (Sandbox Code Playgroud)

Index.cs(隐藏代码)

using StateManagementTest.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace StateManagementTest.Pages
{
    public partial class Index
    {
        public List<Friend> MyFriends { get; set; }
        public List<Friend> SortedFriends { get; set; }

        public List<Follower> MyFollowers { get; set; }
        public List<Follower> SortedFollowers { get; set; }

        private bool IsFriendsDescending { get; set; }
        private bool IsFollowersDescending { get; set; }

        protected override async Task OnInitializedAsync()
        {
            MyFriends = new List<Friend>()
            {
                new Friend(){ FirstName = "Jack", LastName = "Sparrow" },
                new Friend(){ FirstName = "Davey", LastName = "Jones"},
                new Friend(){ FirstName = "Iron", LastName = "Man"}
            };

            MyFollowers = new List<Follower>()
            {
                new Follower(){ Username = "user1234" },
                new Follower(){ Username = "abc123" },
                new Follower(){ Username = "david123"} 
            };

            SortedFriends = MyFriends;
            SortedFollowers = MyFollowers;
        }

        private void SortFriends()
        {
            if (!IsFriendsDescending)
            {
                SortedFriends = MyFriends.OrderByDescending(f => f.FirstName).ToList();
                IsFriendsDescending = true;
                return;
            }
            SortedFriends = MyFriends.OrderBy(f => f.FirstName).ToList();
            IsFriendsDescending = false;
            
        }

        private void SortFollowers()
        {
            if (!IsFollowersDescending)
            {
                SortedFollowers = MyFollowers.OrderByDescending(f => f.Username).ToList();
                IsFollowersDescending = true;
                return;
            }
            SortedFollowers = MyFollowers.OrderBy(f => f.Username).ToList();
            IsFollowersDescending = false;

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

好友列表.razor

<h1>Friends</h1>
@foreach (var friend in Friends)
{
    <div>@friend.FirstName @friend.LastName</div>
}
Run Code Online (Sandbox Code Playgroud)

FriendList.cs(后面的代码)

using Microsoft.AspNetCore.Components;
using StateManagementTest.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace StateManagementTest.Components
{
    public partial class FriendList
    {
        [Parameter]
        public List<Friend> Friends { get; set; }

    }
}
Run Code Online (Sandbox Code Playgroud)

FollowerList.razor

<h1>Followers</h1>
@foreach (var follower in Followers)
{
    <div>@follower.Username</div>
}
Run Code Online (Sandbox Code Playgroud)

FollowerList.cs(背后代码)

using Microsoft.AspNetCore.Components;
using StateManagementTest.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace StateManagementTest.Components
{
    public partial class FollowerList
    {
        [Parameter]
        public List<Follower> Followers { get; set; }

        protected override Task OnParametersSetAsync()
        {
            return base.OnParametersSetAsync();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ugh*_*ffs 5

经过更多的研究以及我的同事也深入研究了这个问题,我们发现了导致这种行为的原因。

我开始再次查看组件生命周期链接,并突出显示了这部分文本:

调用OnParametersSetAsyncOnParametersSet :

  • 在 OnInitialized 或 OnInitializedAsync 中初始化组件后。
  • 当父组件重新渲染并提供时:
    • 仅已知的原始不可变类型,其至少一个参数已更改。
    • 任何复杂类型的参数。框架无法知道复杂类型参数的值是否已在内部发生变化,因此它将参数集视为已更改。

所以这改变了我的问题:是什么导致父组件重新渲染?我本能地开始研究按钮在 Blazor 中的行为方式,并快速阅读了Microsoft 文档,了解 Blazor 中的事件处理,该StateHasChanged处理在每个 UI 事件上调用,无论是onclickonchange等等。因此,当我单击按钮时,它也被调用调用StateHasChanged,这会导致组件重新渲染并重新分配后续子参数,前提是它们是复杂类型。