ASP.NET Core Blazor lifecycle

By Luke Latham and Daniel Roth

The Blazor framework includes synchronous and asynchronous lifecycle methods. Override lifecycle methods to perform additional operations on components during component initialization and rendering.

Lifecycle methods

Before parameters are set

SetParametersAsync sets parameters supplied by the component's parent in the render tree:

public override async Task SetParametersAsync(ParameterView parameters)
{
    await ...

    await base.SetParametersAsync(parameters);
}

ParameterView contains the entire set of parameter values each time SetParametersAsync is called.

The default implementation of SetParametersAsync sets the value of each property with the [Parameter] or [CascadingParameter] attribute that has a corresponding value in the ParameterView. Parameters that don't have a corresponding value in ParameterView are left unchanged.

If base.SetParametersAync isn't invoked, the custom code can interpret the incoming parameters value in any way required. For example, there's no requirement to assign the incoming parameters to the properties on the class.

If any event handlers are set up, unhook them on disposal. For more information, see the Component disposal with IDisposable section.

Component initialization methods

OnInitializedAsync and OnInitialized are invoked when the component is initialized after having received its initial parameters from its parent component in SetParametersAsync.

Use OnInitializedAsync when the component performs an asynchronous operation and should refresh when the operation is completed.

For a synchronous operation, override OnInitialized:

protected override void OnInitialized()
{
    ...
}

To perform an asynchronous operation, override OnInitializedAsync and use the await operator on the operation:

protected override async Task OnInitializedAsync()
{
    await ...
}

Blazor Server apps that prerender their content call OnInitializedAsync twice:

  • Once when the component is initially rendered statically as part of the page.
  • A second time when the browser establishes a connection back to the server.

To prevent developer code in OnInitializedAsync from running twice, see the Stateful reconnection after prerendering section.

While a Blazor Server app is prerendering, certain actions, such as calling into JavaScript, aren't possible because a connection with the browser hasn't been established. Components may need to render differently when prerendered. For more information, see the Detect when the app is prerendering section.

If any event handlers are set up, unhook them on disposal. For more information, see the Component disposal with IDisposable section.

After parameters are set

OnParametersSetAsync or OnParametersSet are called:

  • After the component is initialized in OnInitialized or OnInitializedAsync.
  • When the parent component re-renders and supplies:
    • Only known primitive immutable types of which at least one parameter has changed.
    • Any complex-typed parameters. The framework can't know whether the values of a complex-typed parameter have mutated internally, so it treats the parameter set as changed.
protected override async Task OnParametersSetAsync()
{
    await ...
}

Note

Asynchronous work when applying parameters and property values must occur during the OnParametersSetAsync lifecycle event.

protected override void OnParametersSet()
{
    ...
}

If any event handlers are set up, unhook them on disposal. For more information, see the Component disposal with IDisposable section.

After component render

OnAfterRenderAsync and OnAfterRender are called after a component has finished rendering. Element and component references are populated at this point. Use this stage to perform additional initialization steps using the rendered content, such as activating third-party JavaScript libraries that operate on the rendered DOM elements.

The firstRender parameter for OnAfterRenderAsync and OnAfterRender:

  • Is set to true the first time that the component instance is rendered.
  • Can be used to ensure that initialization work is only performed once.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

Note

Asynchronous work immediately after rendering must occur during the OnAfterRenderAsync lifecycle event.

Even if you return a Task from OnAfterRenderAsync, the framework doesn't schedule a further render cycle for your component once that task completes. This is to avoid an infinite render loop. It's different from the other lifecycle methods, which schedule a further render cycle once the returned task completes.

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        ...
    }
}

OnAfterRender and OnAfterRenderAsync aren't called when prerendering on the server.

If any event handlers are set up, unhook them on disposal. For more information, see the Component disposal with IDisposable section.

Suppress UI refreshing

Override ShouldRender to suppress UI refreshing. If the implementation returns true, the UI is refreshed:

protected override bool ShouldRender()
{
    var renderUI = true;

    return renderUI;
}

ShouldRender is called each time the component is rendered.

Even if ShouldRender is overridden, the component is always initially rendered.

For more information, see ASP.NET Core Blazor WebAssembly performance best practices.

State changes

StateHasChanged notifies the component that its state has changed. When applicable, calling StateHasChanged causes the component to be rerendered.

StateHasChanged is called automatically for EventCallback methods. For more information, see ASP.NET Core Blazor event handling.

Handle incomplete async actions at render

Asynchronous actions performed in lifecycle events might not have completed before the component is rendered. Objects might be null or incompletely populated with data while the lifecycle method is executing. Provide rendering logic to confirm that objects are initialized. Render placeholder UI elements (for example, a loading message) while objects are null.

In the FetchData component of the Blazor templates, OnInitializedAsync is overridden to asychronously receive forecast data (forecasts). When forecasts is null, a loading message is displayed to the user. After the Task returned by OnInitializedAsync completes, the component is rerendered with the updated state.

Pages/FetchData.razor in the Blazor Server template:

@page "/fetchdata"
@using MyBlazorApp.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Handle errors

For information on handling errors during lifecycle method execution, see Handle errors in ASP.NET Core Blazor apps.

Stateful reconnection after prerendering

In a Blazor Server app when RenderMode is ServerPrerendered, the component is initially rendered statically as part of the page. Once the browser establishes a connection back to the server, the component is rendered again, and the component is now interactive. If the OnInitialized{Async} lifecycle method for initializing the component is present, the method is executed twice:

  • When the component is prerendered statically.
  • After the server connection has been established.

This can result in a noticeable change in the data displayed in the UI when the component is finally rendered.

To avoid the double-rendering scenario in a Blazor Server app:

  • Pass in an identifier that can be used to cache the state during prerendering and to retrieve the state after the app restarts.
  • Use the identifier during prerendering to save component state.
  • Use the identifier after prerendering to retrieve the cached state.

The following code demonstrates an updated WeatherForecastService in a template-based Blazor Server app that avoids the double rendering:

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };
    
    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }
    
    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = 
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}

For more information on the RenderMode, see ASP.NET Core Blazor hosting model configuration.

Detect when the app is prerendering



While a Blazor Server app is prerendering, certain actions, such as calling into JavaScript, aren't possible because a connection with the browser hasn't been established. Components may need to render differently when prerendered.

To delay JavaScript interop calls until after the connection with the browser is established, you can use the OnAfterRenderAsync component lifecycle event. This event is only called after the app is fully rendered and the client connection is established.

@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync(
                "setElementText", divElement, "Text after render");
        }
    }
}

For the preceding example code, provide a setElementText JavaScript function inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). The function is called with JSRuntimeExtensions.InvokeVoidAsync and doesn't return a value:

<script>
  window.setElementText = (element, text) => element.innerText = text;
</script>

Warning

The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

The following component demonstrates how to use JavaScript interop as part of a component's initialization logic in a way that's compatible with prerendering. The component shows that it's possible to trigger a rendering update from inside OnAfterRenderAsync. The developer must avoid creating an infinite loop in this scenario.

Where JSRuntime.InvokeAsync is called, ElementRef is only used in OnAfterRenderAsync and not in any earlier lifecycle method because there's no JavaScript element until after the component is rendered.

StateHasChanged is called to rerender the component with the new state obtained from the JavaScript interop call. The code doesn't create an infinite loop because StateHasChanged is only called when infoFromJs is null.

@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(infoFromJs ?? "No value yet")</strong>
</p>

Set value via JS interop call:
<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string infoFromJs;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && infoFromJs == null)
        {
            infoFromJs = await JSRuntime.InvokeAsync<string>(
                "setElementText", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

For the preceding example code, provide a setElementText JavaScript function inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). The function is called withIJSRuntime.InvokeAsync and returns a value:

<script>
  window.setElementText = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Warning

The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

Component disposal with IDisposable

If a component implements IDisposable, the Dispose method is called when the component is removed from the UI. Disposal can occur at any time, including during component initialization. The following component uses @implements IDisposable and the Dispose method:

@using System
@implements IDisposable

...

@code {
    public void Dispose()
    {
        ...
    }
}

Note

Calling StateHasChanged in Dispose isn't supported. StateHasChanged might be invoked as part of tearing down the renderer, so requesting UI updates at that point isn't supported.

Unsubscribe event handlers from .NET events. The following Blazor form examples show how to unhook an event handler in the Dispose method:

  • Private field and lambda approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
        private EventHandler<FieldChangedEventArgs> fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(...);
    
            fieldChanged = (_, __) =>
            {
                ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • Private method approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(...);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    

Cancelable background work

Components often perform long-running background work, such as making network calls (HttpClient) and interacting with databases. It's desirable to stop the background work to conserve system resources in several situations. For example, background asynchronous operations don't automatically stop when a user navigates away from a component.

Other reasons why background work items might require cancellation include:

  • An executing background task was started with faulty input data or processing parameters.
  • The current set of executing background work items must be replaced with a new set of work items.
  • The priority of currently executing tasks must be changed.
  • The app has to be shut down in order to redeploy it to the server.
  • Server resources become limited, necessitating the rescheduling of background work items.

To implement a cancelable background work pattern in a component:

In the following example:

  • await Task.Delay(5000, cts.Token); represents long-running asynchronous background work.
  • BackgroundResourceMethod represents a long-running background method that shouldn't start if the Resource is disposed before the method is called.
@implements IDisposable
@using System.Threading

<button @onclick="LongRunningWork">Trigger long running work</button>

@code {
    private Resource resource = new Resource();
    private CancellationTokenSource cts = new CancellationTokenSource();

    protected async Task LongRunningWork()
    {
        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod();
    }

    public void Dispose()
    {
        cts.Cancel();
        cts.Dispose();
        resource.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod()
        {
            if (disposed)
            {
                throw new ObjectDisposedException(nameof(Resource));
            }
            
            ...
        }
        
        public void Dispose()
        {
            disposed = true;
        }
    }
}