El ciclo de vida de un componente Blazor es la secuencia de métodos que se ejecutan durante su creación, actualización, renderizado y retirada.
Como desarrolladores, necesitamos intervenir en momentos precisos de esta existencia:
- ¿Cuándo cargo los datos de la base de datos?
- ¿Cuándo recalculo variables si el padre cambia un parámetro?
- ¿Cuándo puedo llamar a una librería de JavaScript para pintar un gráfico?
Para intervenir en esos momentos, Blazor ofrece métodos virtuales que podemos sobrescribir.
| Método | Cuándo corre | Objetivo principal |
|---|---|---|
OnInitialized | Al inicializar la instancia | Cargar datos iniciales que no dependen de cambios en parámetros. |
OnParametersSet | 1 vez + cada cambio de parámetros | Recalcular datos basados en parámetros (ProductId, SearchTerm). |
OnAfterRender | Después de cada renderizado interactivo | Llamar a JavaScript, enfocar controles o desplazar la página. |
Dispose | Al retirar el componente | Cancelar tareas y limpiar eventos o temporizadores. |
El flujo general
Antes de entrar en detalle, veamos la secuencia de ejecución estándar cuando un componente se renderiza:
Instanciación: Se crea la clase (Constructor).
SetParametersAsync: Se inyectan los parámetros.
OnInitialized{Async}: Se inicializa el componente.
OnParametersSetAsync: El componente recibe/actualiza datos.
OnAfterRender{Async}: La interfaz interactiva ya se ha actualizado.
Vamos a analizar los tres pilares fundamentales que usarás en el 99% de tus componentes.
OnInitialized y OnInitializedAsync
Este método se ejecuta al inicializar una instancia, después de que haya recibido sus parámetros iniciales. En una aplicación con prerenderizado, la inicialización puede ejecutarse dos veces: una para generar el HTML en el servidor y otra al comenzar el renderizado interactivo.
Es el equivalente al constructor, pero con una diferencia crucial: en el constructor, los [Parameter] y las inyecciones de dependencia (@inject) aún no están listos. En OnInitialized, sí.
¿Para qué lo usamos? Principalmente para cargar datos iniciales de una base de datos o API.
@inject IUserService UserService
<h3>Perfil de @User?.Name</h3>
@code {
private UserDto User;
protected override async Task OnInitializedAsync()
{
// Simulamos una llamada a base de datos
// Hacemos esto aquí y no en el constructor
User = await UserService.GetUserByIdAsync(1);
}
}Usa la variante asíncrona (OnInitializedAsync) cuando esperes una API, una base de datos u otra operación de E/S. Evita bloquear el contexto de ejecución del componente con trabajo síncrono lento.
OnParametersSet y OnParametersSetAsync
En este punto es fácil cometer errores.
Este método se ejecuta:
- Inmediatamente después de
OnInitialized. - Cuando el padre vuelve a proporcionar parámetros. Para tipos complejos, Blazor puede tratarlos como modificados aunque la referencia parezca la misma.
¿Para qué lo usamos?
Para reaccionar a cambios. Imagina un componente que muestra detalles de un producto basado en un Id que le pasa el padre.
- Si cargas el producto en
OnInitialized, solo funcionará la primera vez. - Si el usuario cambia de producto sin salir de la página,
OnInitializedno se volverá a ejecutar, peroOnParametersSetsí.
<h3>Detalle Producto: @Product?.Name</h3>
@code {
[Parameter]
public int ProductId { get; set; }
private ProductDto Product;
// Se ejecuta cada vez que ProductId cambia
protected override async Task OnParametersSetAsync()
{
Product = await ProductService.GetByIdAsync(ProductId);
}
}- Si la carga de datos depende solo de la creación del componente, usa
OnInitialized. - Si la carga de datos depende de un
[Parameter]que puede cambiar, usaOnParametersSet.
OnAfterRender y OnAfterRenderAsync
Este método se ejecuta después de un renderizado interactivo, cuando los cambios ya se han aplicado al DOM y están disponibles las referencias a elementos y componentes. No se ejecuta durante el prerenderizado ni durante SSR estático.
Este método recibe un booleano firstRender:
true: Es la primera vez que se pinta el componente.false: Son actualizaciones posteriores.
¿Para qué lo usamos? Principalmente para inicialización que necesita el contenido ya renderizado, como interactuar con JavaScript mediante JS Interop.
Antes de este punto, los elementos HTML no existen en el navegador. Si intentas llamar a una función JS para pintar un mapa de Google Maps en OnInitialized, fallará porque el <div id="map"> aún no existe.
@inject IJSRuntime JS
<div id="mi-grafico"></div>
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Solo inicializamos librerías JS la primera vez
if (firstRender)
{
await JS.InvokeVoidAsync("iniciarGrafico", "mi-grafico");
}
}
}Cuidado con los renderizados repetidos:
Si llamas a StateHasChanged sin una condición dentro de OnAfterRender, programas otro renderizado y puedes crear un bucle. OnAfterRenderAsync no vuelve a renderizar automáticamente cuando termina su Task, precisamente para evitar este problema.
Liberar recursos con Dispose
Aunque no es un método del ciclo “de vida” activo, es importante para la “muerte”. Si tu componente se suscribe a eventos (de un servicio, por ejemplo) o crea timers, debes limpiarlos cuando el usuario abandona la pantalla.
Para ello, el componente debe implementar IDisposable o IAsyncDisposable.
@implements IDisposable
@code {
protected override void OnInitialized()
{
MiServicio.OnCambio += ManejarCambio;
}
public void Dispose() // Se llama cuando Blazor retira el componente
{
// IMPORTANTE: Desuscribirse para evitar fugas de memoria
MiServicio.OnCambio -= ManejarCambio;
}
}