Andrew
Andrew

Reputation: 5083

Blazor InvokeAsync no longer working after page reload

I have a Blazor page (server-side) that uses an async call to Azure Service Bus to get some data and print the result to the page. Everything works fine at first. However, if the user reloads the page, the page does not get re-rendered as intended.

Debugging with breakpoints shows that the model is updated, and the call to StateHasChanged is executed after the page reload; it just simply doesn't do anything. I was under the impression that InvokeAsync is supposed to always use the UI thread, but that doesn't seem to be the case after a page reload.

protected override async Task OnAfterRenderAsync(bool firstRender)
{
  // On page load. Service bus watcher is hooked up here
  await serviceBusHandler.WatchQueue("servicebus-queue", MessageHandler, async _ => await Task.CompletedTask);
}

// "request" method; calls a Service Bus method get a new value for our model
private async Task SaveAndGetAmount()
{
  IsLoading = true;

  // creation of Service Bus message omitted
  // post to "servicebus-queue" with message here
}

private async Task MessageHandler(ProcessSessionMessageEventArgs args)
{
  var result = ... // parsing of "args" message omitted

  await args.CompleteMessageAsync(args.Message); // mark Azure Functions message received
  
  await InvokeAsync(() =>
  {
    IsLoading = false;
    Model.Amount = result.Amount; // update page model here
    StateHasChanged(); // indicate to UI that the model has changed, and the page must re-render
  });

Additionally, I added a watch to the Model.Amount property and it's exhibiting some weird behavior. If it's set to say 23 on initial page load, the watch will always show 23 on my breakpoint in the SaveAndGetAmount method, as if it's never being updated by the MessageHandler method.

Upvotes: 1

Views: 780

Answers (1)

Henk Holterman
Henk Holterman

Reputation: 273179

WatchQueue does get called on each OnAfterRenderAsync; we ignore firstRender

protected override async Task OnAfterRenderAsync(bool firstRender)
{
  // On page load. Service bus watcher is hooked up here
  await serviceBusHandler.WatchQueue("servicebus-queue", MessageHandler, async _ => await Task.CompletedTask);
}

Just a guess here but this means every (Re)Render adds a messagehandler. That is wrong but goes unnoticed as long as you're on the same instance of the page.

After a reload your messages are still handled on the old (defunct) page. That is what I think you see happening in the debugger.

If I'm right the solution is two-fold:

@implements IDisposable

protected override async Task OnAfterRenderAsync(bool firstRender)
{
  if (firstRender)  // only once per page
  {
    // On page load. Service bus watcher is hooked up here
    await serviceBusHandler.WatchQueue("servicebus-queue", MessageHandler, async _ => await Task.CompletedTask);
  }
}

public void Dispose()
{
  // somehow undo the WatchQueue 
}

If you can't unhook that handler then it's at the wrong place.

Upvotes: 2

Related Questions