No history yet

Component Lifecycle and Logic

The Component Lifecycle

Every Blazor component has a lifecycle, a series of predictable events from the moment it's created to the moment it's destroyed. By tapping into these events, we can load data, react to changes, and clean up resources at precisely the right time. Think of these lifecycle methods as hooks that let us run our C# code at key moments in a component's existence.

Initialization: Sync or Async

Initialization is the first major event. This is your chance to set up the component's initial state, often by fetching data from an API or a database. Blazor provides two methods for this: OnInitialized() and OnInitializedAsync(). The synchronous version, OnInitialized(), is for setup work that doesn't require waiting for anything, like setting a default value for a property.

More commonly, you'll use the asynchronous version, OnInitializedAsync(). This method returns a Task and is the perfect place for any long-running operations, such as making a network request. Using the async version ensures the UI thread isn't blocked, keeping your application responsive while data is being loaded. The component will render an initial version while the async work completes, and then render again once the data arrives.

@page "/weather"
@inject HttpClient Http

<h3>Weather Forecast</h3>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    // ... table to display forecasts ...
}

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        // This network call happens before the component is fully rendered.
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
    }
}

Handling Parameter Changes

Components often receive data from their parents through parameters, marked with the [Parameter] attribute. The OnParametersSet() and OnParametersSetAsync() methods are called when a component first receives its parameters and every time their values change thereafter. This makes them ideal for reacting to new data from a parent component.

For instance, if you have a UserDetails component that receives a UserId, you could use OnParametersSetAsync() to fetch the corresponding user's data whenever the UserId changes. This method is called after OnInitializedAsync on the first render, but it's the go-to method for logic that needs to re-run when parent data is updated.

After the Render

Sometimes you need to run code after the component has finished rendering and the browser's Document Object Model (DOM) has been updated. This is crucial for interacting with JavaScript libraries that need to target specific HTML elements. For this, Blazor gives us OnAfterRender() and OnAfterRenderAsync(). These methods are called after every render cycle.

They receive a boolean parameter, firstRender. This is incredibly useful. You typically only want to perform initial setup, like initialising a chart library, on the very first render. You can check if (firstRender) to ensure that code runs only once. Subsequent calls to OnAfterRenderAsync can then be used for updates.

@page "/chart"
@inject IJSRuntime JSRuntime

<div id="my-chart-container"></div>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // 'initialiseChart' is a function in a loaded JS file.
            await JSRuntime.InvokeVoidAsync("initialiseChart", "my-chart-container");
        }
    }
}

Forcing UI Updates

Blazor is smart about re-rendering. It automatically updates the UI in response to events like button clicks. It does this by comparing a new with the previous one and applying only the differences to the DOM. This is a highly efficient process.

However, there are times when you need to tell Blazor to re-render manually. This happens when a component's state changes because of something outside the normal Blazor UI event flow, like a timer completing or a message arriving from a web socket. In these cases, you call the StateHasChanged() method. This tells the component that its state has been updated and that it needs to re-render to reflect the changes. It signals to Blazor that a new RenderTree should be generated and compared against the old one.

Call StateHasChanged() whenever you update a component's state from outside the standard UI event handlers or component lifecycle methods.

Cleaning Up

Just as components are created, they are also destroyed when a user navigates away from them. A well-behaved component should clean up after itself to prevent memory leaks. This includes unsubscribing from events, disposing of timers, or closing network connections.

To do this, a component can implement the IDisposable or IAsyncDisposable interfaces. Blazor will automatically detect this and call the Dispose() or DisposeAsync() method when the component is removed from the UI. This is the final step in the component's lifecycle, ensuring it leaves no trace behind.

@implements IDisposable

<h3>Timer Component</h3>
<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer? aTimer;

    protected override void OnInitialized()
    {
        aTimer = new Timer(Tick, null, 1000, 1000);
    }

    private void Tick(object? state)
    {
        currentCount++;
        // We need to tell Blazor to re-render from a non-UI thread.
        InvokeAsync(StateHasChanged);
    }

    public void Dispose()
    {
        // This prevents the timer from running after the component is gone.
        aTimer?.Dispose();
    }
}

Understanding this lifecycle is key to building robust and efficient Blazor applications. Let's review the main concepts.

Quiz Questions 1/6

Which lifecycle method is the most appropriate place to perform an asynchronous network request to fetch initial data for a component?

Quiz Questions 2/6

A component's state is updated by an external event, like a message from a WebSocket connection. The UI does not update automatically. Which method should you call to tell Blazor it needs to re-render the component?

Mastering these lifecycle events allows you to precisely control component behaviour, from initial data loading to final resource cleanup.