Blazor: Component doesn't update every time the for loop spins

Viewed 2140

I am currently working on a Blazor project and facing with a bug. The plan is to update a component in every second, as long as the for loop updates. My problem is a bit complex, so I reproduced this bug in another project:

The Calculate.cs file where the calculations happen:

public class Calculate
{
    public void Calc(Index index)
    {
        for (int i = 0; i < 10; i++)
        {
            Thread.Sleep(1000);
            index.Update(i);
        }
    }
}

The index.razor file:

<button class="btn btn-primary" @onclick="Test">Click me</button>

<p>Test number: @testNumber</p>

@code
{
    private int testNumber;
    Calculate c = new Calculate();

    public void Test()
    {
        c.Calc(this);   
    }
    public void Update(int i)
    {
        testNumber = i;
        Debug.WriteLine(i);
    }
}

After testing, the Debug Console prints the numbers as it should in every second, all the way from 0-9, but the opened Blazor display only prints the 9, whenever the calculations and the 10 seconds are over.

3 Answers

This will only work when you can make the process async.

To do that, move the for-loop to the Test() method, and make that a Task returning method. The razor code can stay as-is.

public Task Test()
{
   for (int i = 0; i < 10; i++)
   {
       await Task.Run( _ =>  c.CalcSubTask(this));   

       Update(i);        
       StateHasChanged();
       await Task.Delay(1);
   }
}

and note that this will only perform acceptably in Blazor/Server. In a Wasm project you only have 1 thread and this would lock up your UI.


On a second thought, you could keep the current structure by making the Calc methods async too and await the Update() method. Update() can then await the Task.Delay(1).

Make sure you have a valid await chain from your Test() method through the Calc methods and back to Update().

The update method would look like

public async Task Update(int i)
{
    testNumber = i;
    Debug.WriteLine(i);
    await InvokeAsync(StatHasChanged();  // could be running on another Thread
    await Task.Delay(1);
}

Have you tried to assign i's value to a temp variable inside the for-loop like below?:

var counter = i;
index.Update(counter);
Related