Blazor Component Lifecycle

Understanding the Blazor component lifecycle is crucial for building robust and efficient Blazor applications. It governs how components are initialized, rendered, updated, and disposed of.

Core Lifecycle Stages

Blazor components go through several key stages. Each stage is typically represented by one or more methods that you can override to hook into the component's behavior.

Initialization

This stage involves creating the component instance and setting up its initial state.

Parameter Setting

If the component has parameters, they are set during this stage before the first render.

Rendering

The component's UI is built and rendered for the first time or updated based on state changes.

Disposal

When the component is no longer needed, it is removed from the UI and any resources it holds are released.

Key Lifecycle Methods

1. Initialization

@code {
    protected override void OnInitialized()
    {
        // Perform synchronous initialization tasks
        Console.WriteLine("Component Initialized!");
    }

    protected override async Task OnInitializedAsync()
    {
        // Perform asynchronous initialization tasks
        await Task.Delay(100); // Simulate async work
        Console.WriteLine("Component Initialized Asynchronously!");
    }
}

2. Parameter Setting

@code {
    [Parameter]
    public string Message { get; set; }

    protected override void OnParametersSet()
    {
        base.OnParametersSet(); // Important to call the base method
        Console.WriteLine($"Parameters set: Message = {Message}");
    }
}

3. Rendering

@code {
    private int counter = 0;

    protected override bool ShouldRender()
    {
        // Only re-render if counter is even
        return counter % 2 == 0;
    }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            Console.WriteLine("Component rendered for the first time.");
        }
        else
        {
            Console.WriteLine("Component re-rendered.");
        }
    }

    public void IncrementCounter()
    {
        counter++;
        StateHasChanged(); // Triggers a re-render
    }
}

4. Disposal

@implements IDisposable

@code {
    private Timer _timer;

    protected override void OnInitialized()
    {
        _timer = new Timer(1000);
        _timer.Elapsed += async (sender, e) => await TimerElapsed();
        _timer.Start();
    }

    private async Task TimerElapsed()
    {
        Console.WriteLine("Timer ticked.");
        // Important: If updating state, use InvokeAsync
        // await InvokeAsync(() => StateHasChanged());
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            _timer?.Stop();
            _timer?.Dispose();
            Console.WriteLine("Component disposed.");
        }
    }
}

The Rendering Pipeline

When a component's state changes (e.g., via StateHasChanged() or an event handler), Blazor initiates a rendering process:

  1. The component's diffing process begins.
  2. ShouldRender() is called. If it returns false, rendering stops for this component.
  3. The component's markup is re-evaluated, and a new render tree is generated.
  4. The new render tree is compared with the previous one.
  5. Any differences are applied to the actual DOM.
  6. OnAfterRender() or OnAfterRenderAsync() is called.

Best Practices