Blazorアプリケーションにおけるコンポーネントのライフサイクルメソッドは、レンダリング動作や状態更新に密接に関連しており、正確な実行タイミングを理解することはパフォーマンス最適化やバグ診断に不可欠です。以下では、親子関係を持つ2つのコンポーネントを用いて、各ライフサイクルフックの呼び出し順序および[Parameter]属性付きプロパティへの値の適用タイミングを実証します。
テスト構成
親コンポーネント CounterPage.razor はインタラクティブサーバーモードで動作し、子コンポーネント TraceLifecycleComponent.razor を含みます。両者とも全ライフサイクルメソッドをオーバーライドし、それぞれの実行時に一意のGuidを生成してコンソールに出力します。
子コンポーネント(TraceLifecycleComponent.razor)
@implements IDisposable
<h3>TraceLifecycleComponent</h3>
<div>
<p>Message: @Message, Count: @Count</p>
</div>
@code {
[Parameter] public int Count { get; set; }
[Parameter] public string? Message { get; set; }
protected override bool ShouldRender() =>
(Console.WriteLine($"Child: {nameof(ShouldRender)} ({Guid.NewGuid()})"), true);
public override async Task SetParametersAsync(ParameterView parameters) {
Console.WriteLine($"Child: {nameof(SetParametersAsync)} (before base) - Count={Count}, Message='{Message}'");
await base.SetParametersAsync(parameters);
Console.WriteLine($"Child: {nameof(SetParametersAsync)} (after base) - Count={Count}, Message='{Message}'");
}
protected override void OnInitialized() {
Console.WriteLine($"Child: {nameof(OnInitialized)} - Count={Count}, Message='{Message}'");
}
protected override void OnParametersSet() {
Console.WriteLine($"Child: {nameof(OnParametersSet)} - Count={Count}, Message='{Message}'");
}
protected override void OnAfterRender(bool firstRender) {
Console.WriteLine($"Child: {nameof(OnAfterRender)} - FirstRender={firstRender}");
}
public void Dispose() =>
Console.WriteLine($"Child: {nameof(Dispose)} ({Guid.NewGuid()})");
}親コンポーネント(CounterPage.razor)
@page "/counter"
@rendermode InteractiveServer
@implements IDisposable
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p>Current count: @currentValue</p>
<button class="btn btn-primary" @onclick="Increment">Increment</button>
<br />
<TraceLifecycleComponent Message="@message" Count="currentValue" />
<br />
<input @bind="message" placeholder="Type a message..." />
@code {
private int currentValue = 0;
private string? message = "Initial";
private void Increment() => currentValue++;
protected override bool ShouldRender() {
Console.WriteLine($"Parent: {nameof(ShouldRender)} ({Guid.NewGuid()})");
return base.ShouldRender();
}
public override async Task SetParametersAsync(ParameterView parameters) {
Console.WriteLine($"Parent: {nameof(SetParametersAsync)} (before base)");
await base.SetParametersAsync(parameters);
Console.WriteLine($"Parent: {nameof(SetParametersAsync)} (after base)");
}
protected override void OnInitialized() =>
Console.WriteLine($"Parent: {nameof(OnInitialized)}");
protected override void OnParametersSet() =>
Console.WriteLine($"Parent: {nameof(OnParametersSet)}");
protected override void OnAfterRender(bool firstRender) =>
Console.WriteLine($"Parent: {nameof(OnAfterRender)} - FirstRender={firstRender}");
public void Dispose() =>
Console.WriteLine($"Parent: {nameof(Dispose)} ({Guid.NewGuid()})");
}観測結果と解釈
- 初期ロード時(ページ遷移後初回表示):
親→子の順で
SetParametersAsync→OnInitialized→OnParametersSet→ShouldRender→OnAfterRenderが実行され、その後子→親の順でDisposeが呼び出されます。これは、Blazor Serverにおける初期接続確立前のプレレンダリング段階と、SignalR接続完了後の本格的なインタラクティブモード開始を反映しています。 - 状態変更時(ボタンクリックまたは入力変更):
親コンポーネントの状態変更により、子コンポーネントへ新しいパラメーターが渡される際、
SetParametersAsync内でのbase.SetParametersAsync()呼び出し前にはパラメーター値はまだ反映されておらず、呼び出し後に初めてCountおよびMessageが更新された状態でOnParametersSetが実行されます。 - ナビゲーション時の挙動:
別のページへ遷移すると、現在表示中のすべてのコンポーネントに対して
Disposeが呼び出され、再訪問時には完全に新規インスタンスとして初期化されます。したがって、OnInitializedは「コンポーネント生存期間中1回のみ」ではなく、「アクティブなレンダリングスコープごとに1回」実行されます。
パラメーター束縛のタイミング要約
SetParametersAsyncは、パラメーターの実際の割り当てを担うメソッドであり、その内部でbase.SetParametersAsync()を呼び出すことが必須です。この呼び出し前には[Parameter]プロパティの値は未定義(デフォルト値)のままですが、呼び出し後にはParameterViewから抽出された値が正しく反映されます。そのため、OnInitializedは必ずパラメーター束縛が完了した直後に実行され、OnParametersSetはパラメーターが変更されたたびに呼び出されます(初回含む)。