6
votes

I have a simple Blazor component.

<div @onclick="HandleClick">Click me</div>

@code {

    public async Task HandleClick()
    {
        await Task.Run(()=> System.Threading.Thread.Sleep(1000));
    }

    protected override void OnAfterRender(bool firstRender)
    {
        Console.WriteLine("Rendered");
    }
}

When I click on the div "Rendered" is printed to console and after 1 sec again which means that blazor has rendered component twice. I understand that Blazor triggers an automatic re-render of a component as part of dispatching an event to it.

But why does it rerender after the task is completed? How can I avoid second render?

I have some JS interop in OnAfterRender lifecycle hook that now runs twice. I can add some sort of counter but that would polute my code and I would like to avoid that. I my HandleClick were a simple public void method then everything is ok but that is not always possible

2
It will run when you make HandleClick not async. What you see is rendering before and after the await.Henk Holterman
but why does it render after the task is awaited?partyelite
The UI is flushed before and after await Task.Run, take a look to: stackoverflow.com/questions/56604886/…dani herrera

2 Answers

4
votes

You can use the firstRender variable like this:

if(firstRender)
{
   // Call JSInterop to initialize your js functions, etc.
   // This code will execute only once in the component life cycle.
   // The variable firstRender is true only after the first rendering of the 
   // component; that is, after the component has been created and initialized.
   // Now, when you click the div element firstRender is false, but still the 
   // component is rendered twice, before the awaited method (Task.Run) is called,
   // and after the awaited method completes. The first render occurs because UI 
   // event automatically invoke the StateHasChanged method. The second render 
   // occurs also automatically after an awaited method in an async method 
   // completes. This is how Blazor works, and it shouldn't bother you. 
} 
1
votes

I was facing the same issue but my solution was different than the one mentioned by @enet.

If you are displaying components via loop (for instance foreach loop), you need to set the @key attribute onto the component. In my case I had the same component which was receiving the same type of object with different unique key of course, but Blazor was unable to distinguish between the two. Hence, Blazor was generating all of them when data changed in one of the two.

When You pass @key="Your-unique-key", Blazor watches that key instead of whole model instance.

It's worth reading the Microsoft's note on this: