In a Blazor server component, I use OnInitializedAsync() to connect an event from an injected service:
protected override async Task OnInitializedAsync()
{
_fooRepository.SomethingChanged += OnSomethingChanged;
await Refresh();
}
public void Dispose()
{
_fooRepository.SomethingChanged -= OnSomethingChanged;
}
private async Task Refresh()
{
this.FooData = await LoadDataFromRepository();
}
Unfortunately, according to the documentation, the OnInitializedAsync method may be called twice, depending on the render mode.
I found some examples that recommend using OnAfterRenderAsync for the initialization logic instead:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_fooRepository.SomethingChanged += OnSomethingChanged;
await Refresh();
}
}
[... Dispose and Refresh as above... ]
Are there disadvantages when using OnAfterRenderAsync instead of OnInitializedAsync? It seems to me that I should abandon OnInitializedAsync and default to OnAfterRenderAsync instead.
Note. This answer was updated on 5-Sep-2023 by the author to incorporate comments on OnAfterRender with the advice in point 3 revised, and again on 15 July 2024 to reflect Net 8 Blazor interactivity.
It's important to understand what's going on with pre-rendering.
The page is rendered twice:
blazor.server.js running in the browser session.Points:
OnInitialized{Async} is the natural place for registering event handlers. You should not implement them in OnAfterRender{Async}. Why not? You should register them within the OnInitialized{Async}/OnParametersSet{Async} process, not in a UI event process [see the detailed information below].If you're worried about load time with rendering a page twice, keep the landing page short and sweet.
A final comment. In your code, do the initial load of your data and then register for the SomethingChanged event.
On OnAfterRender
OnAfterRender{Async} is part of the DOM event process, not the OnInitialized{Async}/OnParametersSet{Async} process. It's driven by the Renderer receiving a DOM updated event from the DOM. While OnInitialized{Async}/OnParametersSet{Async} may trigger a render event [through calling StateHasChanged, and thus eventually a OnAfterRender{Async} event once that render has occured], the two are not directly coupled. OnAfterRender{Async} is part of another process, so there is no guarantee when it's run.
In general you should only use OnAfterRender{Async} to do JSInterop stuff. Doing anything in OnAfterRender{Async} that mutates the state of the component [and inevitably necessitates another call the StateHasChanged and another render and another OnAfterRender{Async}] is illogical. All state mutation should happen in OnInitialized{Async}/OnParametersSet{Async}.
Net 8 Interactivity
When a component page SSR renders on the server in Net8 OnAfterRender{Async} isn't called. It therefore seems like a good place to place code that you only want to run when a component is interactively rendered. DON'T: the reasons given above still apply.
Detect pre-rendering in OnInitialized{Async} and code accordingly. There's a Repo here that demonstrates how to detect pre-render. Note that Pre-Render detection is probably going to built in to ComponentBase in Net9.
You can also implement a pre-render splash screen see this question/answer - Blazor .Net 8 splash screen
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With