Aspnetcore: Clearer documentation/samples on loading data and OnInitializedAsync being called twice (Blazor-server)

Created on 5 Feb 2020  路  2Comments  路  Source: dotnet/aspnetcore

I've been looking for examples and documentation on best-practices on how one should load large quantities of data in a razor page/component, when using Blazor server.

The .Net Core 3.1 Blazor docs here suggest you should make an async call to load your data in the OnInitialisedAsync method. They explicitly say that OnInitializedAsync is called once. So I wrote code to do this:

protected override async Task OnInitializedAsync()
{
   FolderService.Instance.OnChange += OnFoldersChanged;
   await LoadFolders();
}

But if I do this, I'm seeing the OnInitializedAsync getting called twice - which ends up executing my long-running data-load method twice too, which wastes time/resources. Another side effect is that my OnChange handler delegate is attached twice, so it gets called twice whenever a change occurs in the service and a dangling delegate will be left even if I'm a good citizen and clean up with FolderService.Instance.OnChange -= OnFoldersChanged in my dispose method. This is very confusing.

There's some talk about this in this SO question, which mentions that the pre-render results in the page being rendered twice - and suggests that it's by design if pre-rendering is enabled (which it is, by default).

After a bit of digging this kindof makes sense - the OnInitializedAsync method is supposed to get called once, and then again when your first await method completes - this is detailed here. In particular, there's a bit of detail that highlights the fact that the second render happens after the _first_ await method completes. The implication is that if I want my page to initially render fast, and then update when the async load of my data is complete, I should put the following in my code:

protected override async Task OnInitializedAsync()
{
   await Task.CompletedTask;
   await LoadFolders();
}

That way the page will render as quickly as possible, and then when my long-running data load method completes, it'll refresh to display the data (assuming it calls StateHasChanged() ).

However, this looks a bit odd, and is very counter-intuitive - anyone reviewing such code would immediately ask "_what is the first await for?!_". Is this a pretty standard thing to do in Blazor server apps?

After a bit of discussion in this Reddit thread the conclusion was reached that the better way to do it is as follows:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
     if (firstRender)
     {
         // Wire up my change handler
         FolderService.Instance.OnChange += OnFoldersChanged;
         // Kick off the data load
         await LoadFolders();
     }
}

That seems to work fine - but this distinction isn't mentioned in the documentation, and I can find no clear examples explaining the nuances of this. Could the Blazor Lifecycle documentation be extended to make this more clear? Or is there a better idiom by which to approach loading data asynchronously at page/component load?

area-blazor

Most helpful comment

Thanks. I never even found the page you linked to, hence the confusion. I've opened this issue to cross-link the docs - an info box from the first page (which most people will see) to the second (which many won't) will probably clear this up. https://github.com/aspnet/AspNetCore.Docs/issues/16860

All 2 comments

@Webreaper thanks for contacting us.

This is documented in depth here

If you think there's something missing. Could you open an issue directly at the bottom of the doc page with what you think its not clear (and if you want, a way to make it better/clearer?) That gets directly routed to our docs and make sure the right people see it.

I'm closing this issue in favor of having a more specific and separate issue in the docs repo itself.

Thanks. I never even found the page you linked to, hence the confusion. I've opened this issue to cross-link the docs - an info box from the first page (which most people will see) to the second (which many won't) will probably clear this up. https://github.com/aspnet/AspNetCore.Docs/issues/16860

Was this page helpful?
0 / 5 - 0 ratings

Related issues

farhadibehnam picture farhadibehnam  路  3Comments

guardrex picture guardrex  路  3Comments

ermithun picture ermithun  路  3Comments

rbanks54 picture rbanks54  路  3Comments

mj1856 picture mj1856  路  3Comments