Blazor コンポーネントのライフサイクル: 完全なガイド
私はしばらく Blazor を使用してきましたが、正直に言って、最初はライフサイクルの方法に戸惑いました。 OnInitialized 対 OnInitializedAsync? OnParametersSet vs OnAfterRender? StateHasChanged が実際に再レンダリングをトリガーするのはいつですか?多くの試行錯誤を経て、最終的にすべてについての確固たるメンタルモデルができました。
ライフサイクルの概要
Blazor コンポーネントがレンダリングされるとき、次のメソッドが順番に実行されます。
SetParametersAsyncOnInitialized/OnInitializedAsyncOnParametersSet/OnParametersSetAsyncOnAfterRender/OnAfterRenderAsyncDispose/DisposeAsync
それぞれについて見ていきましょう。
SetParametersAsync
これは最初に呼び出されるメソッドです。親コンポーネントから生の ParameterView を受け取ります。ほとんどの場合、これをオーバーライドすることはありません。Blazor はマッピング パラメーターを自動的に処理します。ただし、カスタム パラメーターの処理または割り当て前に検証が必要な場合は、次のようにします。
public override async Task SetParametersAsync(ParameterView parameters)
{
// Custom logic before parameters are set
if (parameters.TryGetValue<string>("Title", out var title))
{
Console.WriteLine($"Title is being set to: {title}");
}
await base.SetParametersAsync(parameters);
}
実際のプロジェクトでこれを一度だけ使用しました。ほとんどの場合はスキップします。
OnInitialized / OnInitializedAsync
ここで、データのロード、サービスの初期化、デフォルト値の設定などのセットアップ作業を行います。これは、コンポーネントが最初に作成されたときに 1 回実行されます。
@code {
private List<Product> products = new();
protected override async Task OnInitializedAsync()
{
products = await Http.GetFromJsonAsync<List<Product>>("api/products");
}
}
私をつまずかせた点が 1 つあります。Blazor サーバーでは、プリレンダリング中に OnInitializedAsync が 2 回呼び出されます。 1 回目はサーバー側のプリレンダリング中、2 回目は SignalR 接続の確立時に行われます。 API 呼び出しが高価な場合は、次のように処理することをお勧めします。
private bool isPrerendering = true;
protected override async Task OnInitializedAsync()
{
products = await Http.GetFromJsonAsync<List<Product>>("api/products");
isPrerendering = false;
}
あるいは、PersistentComponentState を使用して二重呼び出しを完全に回避することもできます。
OnParametersSet / OnParametersSetAsync
これは、親コンポーネントが再レンダリングされ、新しいパラメータ値が渡されるたびに起動されます。 OnInitialized の後にも起動されます。ここはパラメータの変更に反応するのに適切な場所です。
[Parameter]
public int CategoryId { get; set; }
private int previousCategoryId;
private List<Product> products = new();
protected override async Task OnParametersSetAsync()
{
if (CategoryId != previousCategoryId)
{
previousCategoryId = CategoryId;
products = await Http.GetFromJsonAsync<List<Product>>(
$"api/products?category={CategoryId}");
}
}
CategoryId != previousCategoryId のチェックは重要です。これがないと、カテゴリが変更されなかったとしても、親が再レンダリングされるたびにデータをリロードすることになります。
OnAfterRender / OnAfterRenderAsync
これは、コンポーネントが DOM にレンダリングされた後に起動されます。 firstRender パラメータは、それが最初のレンダリングであるかどうかを示します。この時点で DOM 要素が存在するため、これが JS Interop 呼び出しの場所です。
[Inject]
private IJSRuntime JS { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("initializeChart", chartElement);
}
}
再レンダリングのたびに JavaScript ライブラリが再初期化されるのを避けるために、私は firstRender をよく使用します。イベント リスナー、チャート ライブラリ、または DOM に直接関わるものを設定している場合は、ここで設定します。
ShouldRender
これはあまり知られていませんが、非常に便利です。 StateHasChanged が呼び出されたときにコンポーネントを再レンダリングするかどうかを制御します。
private bool shouldRender = true;
protected override bool ShouldRender() => shouldRender;
private void HeavyOperation()
{
shouldRender = false;
// Do a bunch of state changes without triggering renders
for (int i = 0; i < 1000; i++)
{
items[i].Process();
}
shouldRender = true;
StateHasChanged(); // Now render once with all changes
}
私はこれを使用して、大きなリストを処理するコンポーネントを最適化しました。項目が変更されるたびに再レンダリングするのではなく、更新をバッチ処理し、最後に 1 回レンダリングします。
破棄 / DisposeAsync
コンポーネントが UI から削除された場合は、リソースをクリーンアップする必要があります。 IDisposable または IAsyncDisposable を実装します。
@implements IAsyncDisposable
@code {
private Timer? timer;
private IJSObjectReference? jsModule;
protected override void OnInitialized()
{
timer = new Timer(OnTick, null, 0, 1000);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
jsModule = await JS.InvokeAsync<IJSObjectReference>("import", "./timer.js");
}
}
public async ValueTask DisposeAsync()
{
timer?.Dispose();
if (jsModule is not null)
{
await jsModule.DisposeAsync();
}
}
}
破棄する一般的なもの: タイマー、イベント サブスクリプション、JS モジュール参照、CancellationTokenSource、および作成した IDisposable サービス。
状態が変化しましたこれはライフサイクル手法ではありませんが、密接に関連しています。これは Blazor に「状態が変わったので、再レンダリングしてください」と伝えます。 Blazor はイベント ハンドラーの後に自動的にこれを呼び出しますが、通常は通常の Blazor イベント フローの外側から状態が変化する場合に、手動で呼び出す必要がある場合があります。
private async Task StartPolling()
{
while (!cts.Token.IsCancellationRequested)
{
data = await Http.GetFromJsonAsync<Data>("api/data");
StateHasChanged(); // Manual call needed since this isn't a Blazor event
await Task.Delay(5000, cts.Token);
}
}
重要な注意事項: Blazor Server のバックグラウンド スレッドから更新している場合は、 InvokeAsync を使用してください。
await InvokeAsync(() =>
{
data = newData;
StateHasChanged();
});
全体像
すべてが起こる順序は次のとおりです。
Component created
└─ SetParametersAsync
└─ OnInitialized / OnInitializedAsync
└─ OnParametersSet / OnParametersSetAsync
└─ Render
└─ OnAfterRender(firstRender: true)
Parameter change from parent
└─ SetParametersAsync
└─ OnParametersSet / OnParametersSetAsync
└─ ShouldRender?
└─ Render
└─ OnAfterRender(firstRender: false)
Component removed
└─ Dispose / DisposeAsync
このフローを頭の中に入れておくと、ライフサイクルの問題のデバッグがはるかに簡単になります。
投稿が気に入っていただければ幸いです!ソーシャルメディアで**@emimontesdeoca**までお気軽にご連絡ください。