yu_ominae
yu_ominae

Reputation: 2935

Blazor state not updatting between child components when using events

I have two independent components in a Blazor wasm app between whichi am trying to communicate. under certain cases the communication fails and I cannot understand why.

The(simplified) setup is as follows

<ParentComponent>
    <HeaderComponent>
        <ProgressBar IsLoading="<Set by IsLoading property from header>" />
    </HeaderComponent>
    <ResultContainer />
</ParentComponent>

The code behind looks something like this:

public class ResultContainerStateManager
{
    public event Action OnLoadStart;

    public event Action OnLoadFinish;

    public NotifyLoadStart() => this.OnLoadStart?.Invoke();

    public NotifyLoadFinish() => this.OnLoadFinish?.Invoke();
}

public partial class HeaderComponent
{
    [Inject]
    public ResultContainerStateManager ResultContainerStateManager { get; set; }

    private bool IsLoading { get; set; }

    protected override void OnInitialized()
    {
          this.ResultContainerStateManager.OnLoadStart += () => this.IsLoading = true;

          this.ResultContainerStateManager.OnLoadFinish += () => this.IsLoading = false;

          base.OnInitializer();
    }
}

public partial class ResultContainer
{
    [Inject]
    public ResultContainerStateManager ResultContainerStateManager { get; set; }

    private bool IsLoading { get; set; }

    protected override async Task OnParametersSetAsync()
    {
          <code>

          if (shouldLoadData)
          {
               this.ResultContainerStateManager.NotifyLoadStart();

               <more code>

               this.ResultContainerStateManager.NotifyLoadFinish();
          }

          await base.OnParametersSetAsync();
    }
}

public partial class ProgressBar
{
     [Parameter]
     public bool IsLoading { get; set; }
}

Where the IsLoading parameter from the progress bar is set from the IsLoading property from HeaderComponent, like

<div id="headerComponent">
    
    <More html here>

    <ProgressBar IsLoading="@this.IsLoading" />
</div>

I don't think it matters, but the progress bar itself uses the MatProgress component, like so:

@if (this.IsLoading)
{
    <MatProgress Indeterminate="true" />
}
else 
{
    <Other html code>
}

The problem is that the progress bar starts when the ResultContainer executes the NotifyLoadStart() method, but it doesn't stop when the NotifyLoadFinish() method is executed. I can see when debugging that the IsLoading property of the HeaderComponent is set back to false after the NotifyLoadFinish() call, but it has no Effect on the UI.

What I have tried so far:

None of that changed anything and I would really like to understand why.

The only success I've had was when using EventCallbacks instead of the events. But I am using events in lots of other places and they all seem to work fine.

Could somebody tell me why events seem to fail and how this can be fixed?

Upvotes: 0

Views: 294

Answers (1)

enet
enet

Reputation: 45764

Try this code

 public async Task OnLoadStart()
    {
        this.IsLoading = true;
        await InvokeAsync(() => { StateHasChanged(); });
    }



 public async Task OnLoadFinish()
    {
        this.IsLoading = false; 
        await InvokeAsync(() => { StateHasChanged(); });
    }

protected override void OnInitialized()
{
     this.ResultContainerStateManager.OnLoadStart += OnLoadStart;
     this.ResultContainerStateManager.OnLoadFinish += OnLoadFinish;
 }

Change : public event Action OnLoadStart;

To: public event Func<Task> OnLoadStart;

And: public event Action OnLoadFinish;

Tp: public event Func<Task> OnLoadFinish;

Implement IDisposable in the HeaderComponent component:

@implements IDisposable

public void Dispose()
{
    this.ResultContainerStateManager.OnLoadStart -= OnLoadStart;
    this.ResultContainerStateManager.OnLoadFinish -= OnLoadFinish;
}

Start coding asynchronously wherever you can.

Upvotes: 1

Related Questions