Reputation: 61
I am currently working on a project using Blazor WebAssembly as my front end framework, however, I am having some complications with state management, I think. This problem was occurring in a large complex solution, so I re-created and simplified it so I could concisely describe it here without all the bloat.
I have a page that contains two components: A list of friends and a list of followers, each with a button to toggle sorting the lists by ascending or descending.
If I override, and put a break point on OnParametersSetAsync inside of the FollowerList component and click the sort button for the FriendList component it hits the break point I set, on the FollowerList component.
I was under the impression that OnParametersSet/Async would only be called on a component if a property fed to that component had changes or it's state had changed. In my code below you can see I have two separate lists, one is being fed to one component and one to the other, so I'm unsure how this method is being triggered on a component that wasn't touched...
Therefore my questions are: why does it hit the break point on the unrelated component? Is this intentional behavior?
Page View
Index page, with the two lists
Index.razor
@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 />
Index.cs (Code behind)
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;
}
}
}
FriendList.razor
<h1>Friends</h1>
@foreach (var friend in Friends)
{
<div>@friend.FirstName @friend.LastName</div>
}
FriendList.cs (Code behind)
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; }
}
}
FollowerList.razor
<h1>Followers</h1>
@foreach (var follower in Followers)
{
<div>@follower.Username</div>
}
FollowerList.cs (Code behind)
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();
}
}
}
Upvotes: 0
Views: 802
Reputation: 61
After some more research and with my colleague also digging into the issue we have found what was causing this behavior.
I started looking at the component life cycle again link here and highlighted this section of text:
OnParametersSetAsync or OnParametersSet are called:
- After the component is initialized in OnInitialized or OnInitializedAsync.
- When the parent component re-renders and supplies:
- Only known primitive immutable types of which at least one parameter has changed.
- Any complex-typed parameters. The framework can't know whether the values of a complex-typed parameter have mutated internally, so it treats the parameter set as changed.
So this changed my question to: what is causing the parent component to re-render? Instinctively I started looking at how buttons behave in Blazor and quickly read on the microsoft docs for event handling in Blazor that StateHasChanged
is called on every UI event, whether that is onclick
, onchange
and so on etc. Therefore, when I was clicking the button it was also calling StateHasChanged
, which caused the component to re-render and reassign subsequent children's parameters, providing they are a complex type.
Upvotes: 5