There is a significant overhead when using events or @bind
in Blazor, as discussed in aspnetcore#18919
Consider this code
Parent.razor
<Child @bind-Value="Value" />
@code {
private int Value { get; set; }
}
Child.razor
<button @onclick="() => Value += 1; ValueChanged.InvokeAsync(Value);">Click me</button>
@code {
[Parameter] public int Value {get;set;}
[Parameter] public EventCallback<int> ValueChanged {get;set;}
}
In this example, clicking the button forces a render of the Parent
component.
This is unnecessary, as the only place where Parent.Value
is used is inside the Child
component, which already has the correct value.
We could therefore avoid the render of Parent
.
The render does not change anything in the DOM, but still evaluates the entire Parent
component, and all its children.
When the number of children grows, this overhead eventually becomes noticable.
Demo (try for yourself on github.io (CSB))
To demonstrate this overhead, there are four components:
BindTo.razor
: The component that contains a button to change the bound value.Reference.razor
: A simple component with just one child. No significant render delay here, as only one parent and one child component are re-rendered unnecessarily.ShowsRenderOverhead.razor
: A component with a lot of child components. There is a significant render delay here, even though only one child actually produces output to the DOM.ShowsNorenderWorkaround.razor
: A component to demonstrate that the culprit is indeed the implicit render: By preventing any reference tothis
in theEventCallback
passed to the child components, we prevent the render cycle.