こちらのサイトによるとBlazorには以下のLifecycleがあるそうです。
- OnInitialized
- OnInitializedAsync
- OnParametersSet
- OnParametersSetAsync
- OnAfterRender
- OnAfterRenderAsync
- ShouldRender
ShouldRender
は画面更新前に呼ばれ、画面更新するかどうかの判断結果をboolで返すそうです。
親ページ内に子コンポーネントが存在する際、親側のShouldRender
がFalse
を返すと子供はその設定に引きずられるのでしょうか?検証してみます。
検証コード
今回の検証はこちらのコードで検証しています。
検証コードの構成と実行画面
検証の実行画面です。
めっちゃダサいけど検証用なのでデザインは許してください…。
画面上部のCheckBoxで親・子のShouldRenderの返り値を制御します。
Count++ボタンを押下するとCount++ボタンを押下した回数をカウントします。 カウントの変数は親コンポーネントが持っています。
子は親からCount値(Count++ボタンを押した回数)をParameterとして受け取り、子の領域内に表示します。
Print Border to Consoleボタンで、Consoleに"-------"という区切り文字を出力します(デバッグ用)
ボタン押下時、自動的に画面更新イベント(StateHasChanged)が実行されます。
各ライフサイクルの関数内で
Console.WriteLine
によるログを出力しています。
以下、親のコードの一部です
public class ParentComponent : ComponentBase { protected int currentCount = 0; protected bool parentRenderFlag = true; protected bool childRenderFlag = true; protected async Task IncrementCount() { currentCount++; Console.WriteLine($"------------------------"); Console.WriteLine($"Parent - Count is {currentCount}"); } protected override void OnInitialized() { Console.WriteLine("Parent - OnInitialized"); } // 途中省略 protected override void OnAfterRender(bool firsttime) { Console.WriteLine($"Parent - OnAfterRender({firsttime})"); } // 途中省略 protected override bool ShouldRender() { Console.WriteLine($"Parent - ShouldRender({parentRenderFlag})"); return parentRenderFlag; } protected void PrintBorderToConsole() { Console.WriteLine("---------------------"); } }
以下、子のコードの一部です
public class ChildComponent : ComponentBase { [Parameter] public int ChildNumber { get; set; } [Parameter] public bool ChildRenderFlag { get; set; } protected override void OnInitialized() { Console.WriteLine("Child - OnInitialized"); } // 途中省略 protected override void OnAfterRender(bool firsttime) { Console.WriteLine($"Child - OnAfterRender({firsttime})"); } // 途中省略 protected override bool ShouldRender() { Console.WriteLine($"Child - ShouldRender({ChildRenderFlag})"); return ChildRenderFlag; } protected void ChildButtonClick() { Console.WriteLine("--- Child Button Clicked ---"); } }
以下、親の画面の一部です
@page "/counter" @inherits LifeCycleSample.Components.ParentComponent <div class="container"> <div class="row"> <div class="col"> <p>Parent count: @currentCount</p> </div> </div> <div class="row"> <div class="col"> <input type="checkbox" name="parentCheckbox" @bind="@parentRenderFlag" /> <label for="parentCheckbox">Parent ShouldRender Flag</label> </div> <!-- 途中省略 --> <div class="col"> <button class="btn btn-primary" @onclick="IncrementCount">Count++</button> </div> </div> <div style="border:1px solid;margin: 10px;padding: 10px"> <ChildParts ChildNumber=@currentCount ChildRenderFlag=@childRenderFlag></ChildParts> </div> <!-- 途中省略 --> </div>
以下、子の画面の一部です
@inherits LifeCycleSample.Components.ChildComponent <div class="row"> <div class="col"> <p>Child count : @ChildNumber</p> </div> </div> <div class="row"> <div class="col"> <button class="btn btn-secondary" @onclick="@ChildButtonClick">Child Print Border to Console</button> </div> </div>
ページ遷移時のLifeCycle
ConsoleのLogは以下の通りです。
Parent - OnInitialized Parent - OnInitializedAsync Parent - OnParametersSet Parent - OnParametersSetAsync Child - OnInitialized Child - OnInitializedAsync Child - OnParametersSet Child - OnParametersSetAsync Parent - OnAfterRender(True) Parent - OnAfterRenderAsync(True) Child - OnAfterRender(True) Child - OnAfterRenderAsync(True)
OnInitialized
->OnParametersSet
->OnAfterRender
の順に実行されています。ShouldRender
は実行されません。OnAfterRender
の引数がTrue、つまり初回アクセスであることを意味します。
画面更新イベントを発行
以下の状態でCount++ボタンを押下して画面更新イベントを発行します。
- 親の
ShouldRender
がTrue
- 子の
ShouldRender
がTrue
ConsoleのLogは以下の通りです。
Parent - Count is 1 Parent - ShouldRender(True) Child - OnParametersSet Child - OnParametersSetAsync Child - ShouldRender(True) Parent - OnAfterRender(False) Parent - OnAfterRenderAsync(False) Child - OnAfterRender(False) Child - OnAfterRenderAsync(False)
Countが1になりました。
親の
ShouldRender
判定後、子のParametersSet
,ShouldRender
,OnAfterRender
が実行されます。親子共に
ShouldRender
の返り値がTrueなので、OnAfterRender
が実行されています。2回目以降の画面更新なので
OnAfterRender
はFalse
になっています。
子のShouldRender無効状態で画面更新イベントを発行
以下の状態でCount++ボタンを押下して画面更新イベントを発行します。
- 親の
ShouldRender
がTrue
- 子の
ShouldRender
がFalse
ConsoleのLogは以下の通りです。
Parent - Count is 2 Parent - ShouldRender(True) Child - OnParametersSet Child - OnParametersSetAsync Child - ShouldRender(False) Parent - OnAfterRender(False) Parent - OnAfterRenderAsync(False)
Countが2になりました。
親のCountは2に表示が更新されましたが、子のCountは1のままです。
子の
ShouldRender
がFalse
になっているので、子のOnAfterRender
が実行されていません。やはり子コンポーネントの画面は更新されていないようです。
親のShouldRender無効状態で画面更新イベントを発行
以下の状態でCount++ボタンを押下して画面更新イベントを発行します。
- 親の
ShouldRender
がFalse
- 子の
ShouldRender
がTrue
ConsoleのLogは以下の通りです。
Parent - Count is 3 Parent - ShouldRender(False)
Countが3になりました。
親も子も画面更新されません。
この状態で子のChild Print Border to Console
ボタンを押下して画面更新イベントを発行しても、画面更新はされませんでした。
おわり
親のShouldRender
がFalse
なら、子のOnParametersSet
が発火せず画面は更新されないようです。
ComponentBaseを継承したクラスのコンストラクタで画面要素を更新するような処理を実行すると例外が発生します。"最初の1回目のアクセスで画面が表示された後"という条件であればOnAfterRender
のfirsttime
がtrue
の時、という条件でなんとかなりそうですね。
OnParametersSet
はパラメータを受け取った前なのか後なのかがよく分かりません。ここはもう少し調査が必要な感じがするので宿題ですね。。