Home > Back-end >  Blazor doesn't detect variable change if it occurs on a separate Thread
Blazor doesn't detect variable change if it occurs on a separate Thread

Time:12-31

In my blazor, I put:

@if (cat.IsMeowing)
{
  <div>Cat is meowing!!!</div>
}

In my Cat class, I put this:

public bool IsMeowing {get; set;} = false;
public void Meow()
{
    Task.Run(async () =>
    {
                await Task.Delay(3000); // Cat takes a deep breath
                IsMeowing = true; // MEOW!!!!!!!
    });
}

The behaviour I want is that when I call Meow, there is a delay, and then it sets the variable which causes the Div to appear. However, blazor does not seem to notice that the variable has updated when it is updated via a closure from another thread.

If I remove the task, and just put the delay and the IsMeowing=true, then blazor notices it and updates correctly.

Is there a way I can get around this without implementing a callback?

Ultimately, I want to create a class that when a method is invoked on it, sets a variable after 3 seconds that blazor notices. Imagine for example that I want to show a message "This operation is taking a while..." if 3 seconds elapses while I'm doing other intensive work, so I can set one of these going, start doing my intensive work, and after my intensive work is done cancel it. If the intensive work took <3 seconds nothing would happen, and if it took more than 3 seconds the message would appear via the blazor if.

CodePudding user response:

The task you create is something external, hence this applies. This is one of the cases when StateHasChanged() must be called. To do that:

Main.razor:

@implements IDisposable

@if (cat.IsMeowing)
{
  <div>Cat is meowing!!!</div>
}
<button @onclick="() => cat.Meow()" title="Poke the cat">Poke the cat</button>
@code {


    public Feline cat = new Feline();
    protected override void OnInitialized()
    {
        cat.UpdateState  = StateHasChanged;
    }

    public void Dispose()
    {
        cat.UpdateState -= StateHasChanged;
    }
}

Feline.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    public class Feline
    {

        public Action UpdateState = null;

        public bool IsMeowing {get; set;} = false;
        
        public void Meow()
        {
            Task.Run(async () =>
            {
                await Task.Delay(3000); // Cat takes a deep breath
                IsMeowing = true; // MEOW!!!!!!!
                if(UpdateState != null)
                    UpdateState();
            });
        }
    }

Example: https://blazorrepl.telerik.com/QmbcnYvw45hRuXES33

Correct me if I am wrong.

CodePudding user response:

Blazor does not 'detect variable change' at all.

But you can easily use the async rendering logic:

//public void Meow()
  public Task Meow()
  {
     Task t1 = ShowMeow();
     Task t2 = longRunningTask(); // or t2 = Task.Run(longRunningTask)
     await Task.WhenAll(t1, t2);
  }

  private Task ShowMeow()
  {
     await Task.Delay(3000); // Cat takes a deep breath
     IsMeowing = true; // MEOW!!!!!!!
  }

When longRunningTask() executes (mostly) synchronous then use Task.Run(), on Blazor-server only.

  • Related