کدهای کامل کامپوننت Pages\LearnBlazor\Lifecycle.razor
@page "/lifecycle" @using System.Threading <div class="border"> <h3>Lifecycles Parent Component</h3> <div class="border"> <LifecycleChild CountValue="CurrentCount"></LifecycleChild> </div> <p>Current count: @CurrentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> <br /><br /> <button class="btn btn-primary" @onclick=StartCountdown>Start Countdown</button> @MaxCount </div> @code { int CurrentCount = 0; int MaxCount = 5; private void IncrementCount() { CurrentCount++; Console.WriteLine("Parnet - IncrementCount is called"); } protected override void OnInitialized() { Console.WriteLine("Parnet - OnInitialized is called"); } protected override async Task OnInitializedAsync() { await Task.Delay(100); Console.WriteLine("Parnet - OnInitializedAsync is called"); } protected override void OnParametersSet() { Console.WriteLine("Parnet - OnParameterSet is called"); } protected override async Task OnParametersSetAsync() { await Task.Delay(100); Console.WriteLine("Parnet - OnParametersSetAsync is called"); } protected override void OnAfterRender(bool firstRender) { if (firstRender) { Console.WriteLine("Parnet - OnAfterRender(firstRender == true) is called"); CurrentCount = 111; } else { CurrentCount = 999; Console.WriteLine("Parnet - OnAfterRender(firstRender == false) is called"); } } protected override async Task OnAfterRenderAsync(bool firstRender) { await Task.Delay(100); Console.WriteLine("Parnet - OnAfterRenderAsync is called"); } protected override bool ShouldRender() { Console.WriteLine("Parnet - ShouldRender is called"); return true; } void StartCountdown() { Console.WriteLine("Parnet - StartCountdown()"); var timer = new Timer(TimeCallBack, null, 1000, 1000); } void TimeCallBack(object state) { if (MaxCount > 0) { MaxCount--; Console.WriteLine("Parnet - InvokeAsync(StateHasChanged)"); InvokeAsync(StateHasChanged); } } }
و کدهای کامل کامپوننت Pages\LearnBlazor\LearnBlazorComponents\LifecycleChild.razor
<h3 class="ml-3 mr-3">Lifecycles Child Componenet</h3> @code { [Parameter] public int CountValue { get; set; } protected override void OnInitialized() { Console.WriteLine(" Child - OnInitialized is called"); } protected override async Task OnInitializedAsync() { await Task.Delay(100); Console.WriteLine(" Child - OnInitializedAsync is called"); } protected override void OnParametersSet() { Console.WriteLine(" Child - OnParameterSet is called"); } protected override async Task OnParametersSetAsync() { await Task.Delay(100); Console.WriteLine(" Child - OnParametersSetAsync is called"); } protected override void OnAfterRender(bool firstRender) { if (firstRender) { Console.WriteLine(" Child - OnAfterRender(firstRender == true) is called"); } else { Console.WriteLine(" Child - OnAfterRender(firstRender == false) is called"); } } protected override async Task OnAfterRenderAsync(bool firstRender) { await Task.Delay(100); Console.WriteLine(" Child - OnAfterRenderAsync is called"); } protected override bool ShouldRender() { Console.WriteLine(" Child - ShouldRender is called"); return true; } }
<li class="nav-item px-3"> <NavLink class="nav-link" href="lifecycle"> <span class="oi oi-list-rich" aria-hidden="true"></span> Lifecycles </NavLink> </li>
رویدادهای OnInitialized و OnInitializedAsync
@code { protected override void OnInitialized() { Console.WriteLine("Parnet - OnInitialized is called"); } protected override async Task OnInitializedAsync() { await Task.Delay(100); Console.WriteLine("Parnet - OnInitializedAsync is called"); }
در کامپوننت Lifecycle.razor، یک کامپوننت دیگر نیز به نام LifecycleChild.razor فراخوانی شدهاست. در این حالت ابتدا OnInitialized کامپوننت والد فراخوانی شدهاست و پس از آن بلافاصله فراخوانی OnInitialized کامپوننت فرزند را مشاهده میکنیم.
رویدادهای OnParametersSet و OnParametersSetAsync
این رویدادها یکبار در زمان بارگذاری اولیهی کامپوننت و بار دیگر هر زمانیکه کامپوننت فرزند، پارامتر جدیدی را از طریق کامپوننت والد دریافت میکند، فراخوانی میشوند. برای نمونه کامپوننت LifecycleChild، پارامتر CurrentCount را از والد خود دریافت میکند:
<LifecycleChild CountValue="CurrentCount"></LifecycleChild>
Parnet - IncrementCount is called Parnet - ShouldRender is called Child - OnParameterSet is called Child - ShouldRender is called Parnet - OnAfterRender(firstRender == false) is called Child - OnAfterRender(firstRender == false) is called Child - OnParametersSetAsync is called Child - ShouldRender is called Child - OnAfterRender(firstRender == false) is called Child - OnAfterRenderAsync is called Parnet - OnAfterRenderAsync is called Child - OnAfterRenderAsync is called
رویدادهای OnAfterRender و OnAfterRenderAsync
پس از هر بار رندر کامپوننت، این متدها فراخوانی میشوند. در این مرحله کار بارگذاری کامپوننت، دریافت اطلاعات و نمایش آنها به پایان رسیدهاست. یکی از کاربردهای آن، آغاز کامپوننتهای جاوا اسکریپتی است که برای کار، نیاز به DOM را دارند؛ مانند نمایش یک modal بوت استرپی.
یک نکته: هر تغییری که در مقادیر فیلدها در این رویدادها صورت گیرند، به UI اعمال نمیشوند؛ چون در مرحلهی آخر رندر UI قرار دارند.
@code { protected override void OnAfterRender(bool firstRender) { if (firstRender) { Console.WriteLine("Parnet - OnAfterRender(firstRender == true) is called"); CurrentCount = 111; } else { CurrentCount = 999; Console.WriteLine("Parnet - OnAfterRender(firstRender == false) is called"); } } protected override async Task OnAfterRenderAsync(bool firstRender) { await Task.Delay(100); Console.WriteLine("Parnet - OnAfterRenderAsync is called"); } }
سؤال: با توجه به مقدار دهیهای 111 و 999 صورت گرفتهی در متد OnAfterRender، در اولین بار نمایش کامپوننت، چه عددی به عنوان CurrentCount نمایش داده میشود؟
در اولین بار نمایش صفحه، لحظهای عدد 111 و سپس عدد 999 نمایش داده میشود. عدد 111 را در بار اول رندر و عدد 999 را در بار دوم رندر که پس از مقدار دهی پارامتر کامپوننت فرزند است، میتوان مشاهده کرد.
اما ... اگر پس از نمایش اولیهی صفحه، چندین بار بر روی دکمهی click me کلیک کنیم، همواره عدد 1000 مشاهده میشود. علت اینجا است که تغییرات مقادیر فیلدها در متد OnAfterRender، به UI اعمال نمیشوند؛ چون در این مرحله، رندر UI به پایان رسیدهاست. در اینجا فقط مقدار فیلد CurrentCount به 999 تغییر میکند و به همین صورت باقی میماند. دفعهی بعدی که بر روی دکمهی click me کلیک میکنیم، یک واحد به آن اضافه شده و اکنون است که کار رندر UI، مجددا شروع خواهد شد (در واکشن به یک رخداد و فراخوانی ضمنی StateHasChanged در پشت صحنه) و اینبار حاصل 999+1 را در UI مشاهده میکنیم و باز هم در پایان کار رندر، مجددا مقدار CurrentCount به 999 تغییر میکند که ... دیگر به UI منعکس نمیشود تا زمان کلیک بعدی و همینطور الی آخر.
رویدادهای StateHasChanged و ShouldRender
- اگر خروجی رویداد ShouldRender مساوی true باشد، اجازهی اعمال تغییرات به UI داده خواهد شد و برعکس. بنابراین اگر حالت UI تغییر کند و خروجی این متد false باشد، این تغییرات نمایش داده نخواهند شد.
- اگر رویداد StateHasChanged فراخوانی شود، به معنای درخواست رندر مجدد UI است. کاربرد آن در مکانهایی است که نیاز به اطلاع رسانی دستی تغییرات UI وجود دارد؛ درست پس از زمانیکه رندر UI به پایان رسیدهاست. برای آزمایش این مورد و فراخوانی دستی StateHasChanged، کدهای تایمر زیر تهیه شدهاند:
@page "/lifecycle" @using System.Threading button class="btn btn-primary" @onclick=StartCountdown>Start Countdown</button> @MaxCount @code { int MaxCount = 5; void StartCountdown() { Console.WriteLine("Parnet - StartCountdown()"); var timer = new Timer(TimeCallBack, null, 1000, 1000); } void TimeCallBack(object state) { if (MaxCount > 0) { MaxCount--; Console.WriteLine("Parnet - InvokeAsync(StateHasChanged)"); InvokeAsync(StateHasChanged); } } }
یک نکته: متدهای رویدادگردان در Blazor، میتوانند sync و یا async باشند؛ مانند متدهای OnClick و OnClickAsync زیر که هر دو پس از پایان متدها، سبب فراخوانی ضمنی StateHasChanged نیز میشوند (به این دلیل است که با کلیک بر روی دکمهای، UI هم به روز رسانی میشود). البته متدهای رویدادگردان async، دوبار سبب فراخوانی ضمنی StateHasChanged میشوند؛ یکبار زمانیکه قسمت sync متد به پایان میرسد و یکبار هم زمانیکه کار فراخوانی کلی متد به پایان خواهد رسید:
<button @onclick="OnClick">Synchronous</button> <button @onclick="OnClickAsync">Asynchronous</button> @code{ void OnClick() { } // StateHasChanged is called after the method async Task OnClickAsync() { text = "click1"; // StateHasChanged is called here as the synchronous part of the method ends await Task.Delay(1000); await Task.Delay(2000); text = "click2"; } // StateHasChanged is called after the method }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-06.zip