Blazor コンポーネントライフサイクルの実行順序とパラメーター束縛の検証

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()})");
}

観測結果と解釈

  • 初期ロード時(ページ遷移後初回表示): 親→子の順でSetParametersAsyncOnInitializedOnParametersSetShouldRenderOnAfterRenderが実行され、その後子→親の順でDisposeが呼び出されます。これは、Blazor Serverにおける初期接続確立前のプレレンダリング段階と、SignalR接続完了後の本格的なインタラクティブモード開始を反映しています。
  • 状態変更時(ボタンクリックまたは入力変更): 親コンポーネントの状態変更により、子コンポーネントへ新しいパラメーターが渡される際、SetParametersAsync内でのbase.SetParametersAsync()呼び出し前にはパラメーター値はまだ反映されておらず、呼び出し後に初めてCountおよびMessageが更新された状態でOnParametersSetが実行されます。
  • ナビゲーション時の挙動: 別のページへ遷移すると、現在表示中のすべてのコンポーネントに対してDisposeが呼び出され、再訪問時には完全に新規インスタンスとして初期化されます。したがって、OnInitializedは「コンポーネント生存期間中1回のみ」ではなく、「アクティブなレンダリングスコープごとに1回」実行されます。

パラメーター束縛のタイミング要約

SetParametersAsyncは、パラメーターの実際の割り当てを担うメソッドであり、その内部でbase.SetParametersAsync()を呼び出すことが必須です。この呼び出し前には[Parameter]プロパティの値は未定義(デフォルト値)のままですが、呼び出し後にはParameterViewから抽出された値が正しく反映されます。そのため、OnInitializedは必ずパラメーター束縛が完了した直後に実行され、OnParametersSetはパラメーターが変更されたたびに呼び出されます(初回含む)。

タグ: Blazor aspnet-core component-lifecycle parameter-binding interactive-server

6月13日 21:26 投稿