Add StateHasChanged(async: true) that guarantees never to run synchronously
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 276
Description
The StateHasChanged method is supposed to flag the component to be re-rendered, so if you call this method multiple times from the same call, it should render the component only once.
Actually, this is working ok when the call is performed from a Blazor event callback.
If I have a component names Component1 and the following markup in Index.razor
```
@page "/"
Update Component (From Blazor)
@code {
Component1 component1;
private void UpdateComponent()
{
component1.UpdateTheComponent();
}
}
```
and the component code is the following
```
Component1
@{
System.Diagnostics.Debug.WriteLine("ComponentRendered");
}
@code {
public void UpdateTheComponent()
{
for (int i = 0; i < 100; i++)
{
StateHasChanged();
}
}
}
```
The text written in the output of visual studio is
> "ComponentRendered"
Only one time.
If instead of calling the UpdateTheComponent() method from the Blazor button handler, it is called from JavaScript, the component is updated multiple times.
To call the UpdateTheComponent() method from javascript, I will alter the component to pass the component reference to a JavaScript method.
```
@inject IJSRuntime JS
Component1
Update Component (From JS)
@{
System.Diagnostics.Debug.WriteLine("ComponentRendered");
}
@code {
ElementReference button;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var ComponentReference = new Component1Reference(this);
await JS.InvokeVoidAsync("Component1.init", button, DotNetObjectReference.Create(ComponentReference));
}
}
public void UpdateTheComponent()
{
for (int i = 0; i < 100; i++)
{
StateHasChanged();
}
}
public class Component1Reference
{
private Component1 Component1;
internal Component1Reference(Component1 scrollViewer)
{
Component1 = scrollViewer;
}
[JSInvokable]
public void UpdateTheComponent()
{
Component1.UpdateTheComponent();
}
}
}
```
and the scripts.js javascript having the following
```
window.Component1 = {
init: function (elementReference, componentReference) {
elementReference.Component1 = componentReference;
},
updateComponent: function (element) {
element.Component1.invokeMethodAsync('UpdateTheComponent');
}
}
```
When I press the button, the javascript obtains the component reference and call to the Blazor method one time.
But this time, the component is rendered multiple times
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
> ComponentRendered
In many scenarios this causes dramatic performance degradation, as the rendering is executed multiple times unnecessarily.
[BlazorApp8.zip](https://github.com/dotnet/aspnetcore/files/4670113/BlazorApp8.zip)
Contributor guide
Assessment
This issue has not been assessed yet.