-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Is there an existing issue for this?
- I have searched the existing issues
Is your feature request related to a problem? Please describe the problem.
UI's with many components like lists, trees and tables can perform really bad, because too many components gets rendered at every update. The Blazor change detecting easily gets thrown off because of event handlers like:
@foreach (var item in items)
{
<Item OnClick="@(() => HandleClick(item))">Click me</Item>
}
Because the event handler is a new delegate object each render the whole Item component also gets rendered.
One fix is to implement ShouldRender in the Item component, but that gets really tedious.
I have made my own RenderBlocker component that simply takes a Key and uses it to evaluate if a new render is needed. Internally it uses ShouldRender and compares the Key to previous render.
Used like this:
@foreach (var item in items)
{
<RenderBlocker Key="@item">
<Item OnClick="@(() => HandleClick(item))">Click me</Item>
</RenderBlocker>
}
Describe the solution you'd like
The suggestion is to add a @renderkey to the razor syntax that behaves exactly like my custom RenderBlocker component.
@foreach (var item in items)
{
<Item @renderkey="@item" OnClick="@(() => HandleClick(item))">Click me</Item>
}
Maybe extended with custom comparer:
@foreach (var item in items)
{
<Item @renderkey="@item" @renderkey:comparer="@MyComparer" OnClick="@(() => HandleClick(item))">Click me</Item>
}
Because item is the same as last render, the whole component is re-used. (And also sub components)
Additional context
Note that the existing @key doesn't solve this issue, but my suggestion with @renderkey follows a similar syntax.
The code for RenderBlocker in case anyone needs it:
@typeparam TKey
@ChildContent
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public TKey? Key { get; set; }
[Parameter]
public IEqualityComparer<TKey>? Comparer { get; set; }
private TKey? previousKey;
private bool firstFlag = true;
protected override void OnAfterRender(bool firstRender)
{
firstFlag = false;
previousKey = Key;
}
protected override bool ShouldRender()
{
if (firstFlag) return true;
if ((Comparer ?? EqualityComparer<TKey>.Default).Equals(Key, previousKey))
{
return false;
}
return true;
}
}