Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blazor doesn't detect variable change if it occurs on a separate Thread

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.

like image 775
NibblyPig Avatar asked Mar 17 '26 03:03

NibblyPig


2 Answers

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 += UpdateState;
    }

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

    private void UpdateState()
    {
        InvokeAsync(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/cHOGmxPu44pZTDEm38

Correct me if I am wrong.

like image 114
maciek Avatar answered Mar 19 '26 17:03

maciek


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.

like image 24
Henk Holterman Avatar answered Mar 19 '26 16:03

Henk Holterman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!