Version information
.NET Core SDK: 3.1.302
ASP.NET Core Blazor WebAssembly: 3.2.1
Demo Application
To get started right away, I use the demo application that comes with the SDK. To do so, we create a new Blazor WebAssembly project and make sure it works properly.
				
					dotnet new blazorwasm -o BlazorApp1
cd BlazorApp1
dotnet run 
				
			
		First, we implement a new component to visualize the lifecycle. Create a new file DemoComponent.razor in the Shared folder, which overrides (almost) all methods. The only method we ignore is ShouldRender, which is useful for some advanced use cases (like performance optimization). We may talk about ShouldRender in the future.
				
					@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger Logger
Demo Component
@code {
   public DemoComponent()
   {
       // "Logger" is not initialized yet
   }
   public override async Task SetParametersAsync(ParameterView parameters)
   {
       Logger.LogInformation("SetParametersAsync-start");
       await base.SetParametersAsync(parameters);
       Logger.LogInformation("SetParametersAsync-end");
   }
   protected override void OnInitialized()
   {
       Logger.LogInformation("OnInitialized-start");
       base.OnInitialized();
       Logger.LogInformation("OnInitialized-end");
   }
   protected override async Task OnInitializedAsync()
   {
       Logger.LogInformation("OnInitializedAsync-start");
       await base.OnInitializedAsync();
       Logger.LogInformation("OnInitializedAsync-end");
   }
   protected override void OnParametersSet()
   {
       Logger.LogInformation("OnParametersSet-start");
       base.OnParametersSet();
       Logger.LogInformation("OnParametersSet-end");
   }
   protected override async Task OnParametersSetAsync()
   {
       Logger.LogInformation("OnParametersSetAsync-start");
       await base.OnParametersSetAsync();
       Logger.LogInformation("OnParametersSetAsync-end");
   }
   protected override void OnAfterRender(bool firstRender)
   {
       Logger.LogInformation("OnAfterRender({firstRender})-start", firstRender);
       base.OnAfterRender(firstRender);
       Logger.LogInformation("OnAfterRender({firstRender})-end", firstRender);
   }
   protected override async Task OnAfterRenderAsync(bool firstRender)
   {
       Logger.LogInformation("OnAfterRenderAsync({firstRender})-start", firstRender);
       await base.OnAfterRenderAsync(firstRender);
       Logger.LogInformation("OnAfterRenderAsync({firstRender})-end", firstRender);
   }
   public void Dispose()
   {
       Logger.LogInformation("Dispose");
   }
}  
				
			
		Now, we put the component onto the page Index.razor.
				
					@page "/"
 
				
			
		The component renders text only.
 
													The logger output of the component should look something like the following:
				
					SetParametersAsync-start
    OnInitialized-start
    OnInitialized-end
    OnInitializedAsync-start
    OnInitializedAsync-end
    OnParametersSet-start
    OnParametersSet-end
    OnParametersSetAsync-start
    OnParametersSetAsync-end
SetParametersAsync-end
OnAfterRender(True)-start
OnAfterRender(True)-end
OnAfterRenderAsync(True)-start
OnAfterRenderAsync(True)-end 
				
			
		Blazor Component Lifecycle
The lifecycle of a Blazor component begins when it is rendered on the page, meaning that it becomes visible for the first time. This might happen after navigating to a page with the corresponding component or by the evaluation of statements like if to true.
For example, if the variable _renderDemoComponent is false after navigating to a page then the DemoComponent will not be created until the if statement evaluates to true. Then again, if _renderDemoComponent is true and becomes false then the DemoComponent will not just be hidden but destroyed. The same happens when navigating to another page, which leads to the disposal of all components of the current page.
				
					@if (_renderDemoComponent)
{
     
				
			
		The canonical lifecycle of a Blazor component is pretty linear, at least on initial rendering. It looks like this:
- Constructor
- SetParametersAsync
- OnInitialized/- OnInitializedAsync
- OnParametersSet/- OnParametersSetAsync
- OnAfterRender/- OnAfterRenderAsync
- IDisposable.Dispose()(if we navigate to another page)
Note: The interface IAsyncDisposable is not supported.
The Easy Parts about the Lifecycle
Some methods are called only once, which makes it easier to understand the lifecycle. The first one is the Constructor, which is more or less useless in Blazor components because the constructor dependency injection is not supported. The other is a method pair OnInitialized/OnInitializedAsync, which is called after the initial(!) setting of the parameters. This method pair is kind of the constructor of a Blazor component and is useful for the initialization of the component. The last one is the method Dispose, which is called if the component is implementing the interface IDisposable. This method should be used for all kinds of cleanup, like unsubscribing from events and for stopping asynchronous calls.
The Misleading Parts about the Lifecycle
Although the method Dispose is called last, there is no warranty that all previously started asynchronous calls (like in OnAfterRenderAsync) are finished. All asynchronous calls should have CancellationToken support and be canceled in Dispose.
One of the programming behaviors I see very often is overriding a virtual method without calling the base method. Most of the time, this poses no impact on the component because the base methods are empty, but the method SetParametersAsync is different.
If the base method SetParametersAsync is not called …
				
					public override async Task SetParametersAsync(ParameterView parameters)
{
   Logger.LogInformation("SetParametersAsync-start");
   //await base.SetParametersAsync(parameters);
   Logger.LogInformation("SetParametersAsync-end");
} 
				
			
		…then the lifecycle is broken because subsequent methods like OnInitialized are not called and the component is not rendered.
 
													The Complex Parts about the Lifecycle
The remaining three methods, or method pairs, may be called multiple times during the lifetime of a component.
In general, if there is an asynchronous call, the lifecycle gets messy because the calls may overlap. To see this in action, we add an asynchronous method to OnInitializedAsync.
				
					protected override async Task OnInitializedAsync()
{
   Logger.LogInformation("OnInitializedAsync-start");
   await base.OnInitializedAsync();
   await Task.Yield();
   Logger.LogInformation("OnInitializedAsync-end");
} 
				
			
		According to the logger output below, the lifecycle changed quite a lot. The (initial) rendering comes before OnParametersSet and is triggered twice in total. Before seeing this, the method OnParametersSet (due to its name) may look more suitable for initial(!) processing of the parameters, but that’s not always the case. As we can see, the method OnParametersSet should not be preferred to OnInitialized/OnInitializedAsync because otherwise the component will be rendered without initialization.
				
					SetParametersAsync-start
    OnInitialized-start
    OnInitialized-end
    
    OnInitializedAsync-start
    
        OnAfterRender(True)-start
        OnAfterRender(True)-end
        
        OnAfterRenderAsync(True)-start
        OnAfterRenderAsync(True)-end
    
    OnInitializedAsync-end
    
    OnParametersSet-start
    OnParametersSet-end
    
    OnParametersSetAsync-start
    OnParametersSetAsync-end
    
    OnAfterRender(False)-start
    OnAfterRender(False)-end
    
    OnAfterRenderAsync(False)-start
    OnAfterRenderAsync(False)-end
SetParametersAsync-end 
				
			
		Another pitfall is to assume that the method SetParametersAsync is called every time the parameters are changed, but that is not always true. Under some conditions, the method is called even if the parameters are still the same.
For more information see: Don’t create components that write to their own parameter properties.
Last but not least, there is the method-pair OnAfterRender/OnAfterRenderAsync, which is called more often than the rest of the lifecycle methods. In previous chapters, we saw that asynchronous calls may change the order in which the methods are called, but that is not all. What I did not point out before is that the methods may overload with its previous calls. In other words, the method pair OnAfterRender/OnAfterRenderAsync may be called (again) although the previous execution of OnAfterRenderAsync is not finished yet.
To demonstrate the overlapping, we add a <script> at the end of the index.html.
				
					   ...
     
     
 