Blazor Lifecycle Demo
Parent Value: 0
Child Value: 0(EnableFilter: False)
Child Logs:
Xem code
Parent page(LifecycleDemo.razor)
@page "/lifecycle-demo"
<div class="border rounded p-3">
@*Cài đặt tham số cho LifeCycle của child Component*@
<div class="form-check ms-2">
<input class="form-check-input" type="checkbox" id="chkOI" @bind="_useAwaitInChild_OnInitializedAsync"/>
<label class="form-check-label" for="chkOI">Chạy Await trong hàm OnInitializedAsync của Child Component</label>
</div>
<div class="form-check ms-2">
<input class="form-check-input" type="checkbox" id="chkOP" @bind="_useAwaitInChild_OnParametersSetAsync"/>
<label class="form-check-label" for="chkOP">Chạy Await trong hàm OnParametersSetAsync của Child Component</label>
</div>
<div class="form-check ms-2">
<input class="form-check-input" type="checkbox" id="chkOA" @bind="_useAwaitInChild_OnAfterRenderAsync"/>
<label class="form-check-label" for="chkOA">Chạy Await trong hàm OnAfterRenderAsync của Child Component</label>
</div>
<div class="form-check ms-2">
<input class="form-check-input" type="checkbox" id="chkES" @bind="_enableChildShouldRenderFilter"/>
<label class="form-check-label" for="chkES">Bật bộ lọc ShouldRender của Child Component</label>
</div>
@*Tham số truyền vào child component*@
<div class="d-flex align-items-center gap-2 flex-wrap">
<button class="btn btn-primary" @onclick="Inc">Value++</button>
<button class="btn btn-outline-secondary" @onclick="ToggleChild">Toggle Child (mount/unmount)</button>
</div>
<p class="mt-3 mb-2"><b>Parent Value:</b> @_value</p>
</div>
<span class="text-muted">*Lưu ý: Khi child component bị unmount thì sẽ gọi đến Dispose (nếu có) → có thể dùng để cleanup resource, hủy subscription,...*</span>
<div class="border rounded p-3">
@if (_showChild)
{
<div>
<LifecycleChild Value="@_value" EnableShouldRenderFilter="@_enableChildShouldRenderFilter"
UseAwaitInOnInitializedAsync="@_useAwaitInChild_OnInitializedAsync"
UseAwaitInOnParametersSetAsync="@_useAwaitInChild_OnParametersSetAsync"
UseAwaitInOnAfterRenderAsync="@_useAwaitInChild_OnAfterRenderAsync"/>
</div>
}
else
{
<p class="text-muted mb-0">Child đang bị unmount → nếu trước đó có <code>Dispose</code> thì sẽ chạy.</p>
}
</div>
@code{
private bool _useAwaitInChild_OnInitializedAsync = false;
private bool _useAwaitInChild_OnParametersSetAsync = true;
private bool _useAwaitInChild_OnAfterRenderAsync = false;
private bool _enableChildShouldRenderFilter = false;
private int _value = 0;
private bool _showChild = true;
private void Inc()
{
_value++;
}
private void ToggleChild()
{
_showChild = !_showChild;
}
}
Child Component (LifecycleChild.razor)
@implements IDisposable
<div class="border rounded p-3 bg-light">
<p class="mb-1"><b>Child Value:</b> @Value<span class="text-muted">(EnableFilter: @EnableShouldRenderFilter)</span></p>
<div>
<b>Child Logs:</b>
<ul class="mb-0">
@foreach (var line in _displayLogs)
{
<li>@line</li>
}
</ul>
<button class="btn btn-sm btn-outline-secondary mt-2" @onclick="() => _displayLogs = new List<string>(_logs)">Refresh Logs</button>
<button class="btn btn-sm btn-outline-secondary mt-2" @onclick="() => _clearLogsBeforeNextRender = true">Clear Logs</button>
</div>
</div>
@code
{
[Parameter] public int Value { get; set; }
[Parameter] public bool UseAwaitInOnInitializedAsync { get; set; }
[Parameter] public bool UseAwaitInOnParametersSetAsync { get; set; }
[Parameter] public bool UseAwaitInOnAfterRenderAsync { get; set; }
[Parameter] public bool EnableShouldRenderFilter { get; set; }
private bool _clearLogsBeforeNextRender = false;
private List<string> _logs = new();
private List<string> _displayLogs = new();
private int _renderCount = 0;
public override async Task SetParametersAsync(ParameterView parameters)
{
Log("Child-SetParametersAsync() - start");
await base.SetParametersAsync(parameters);
Log("Child-SetParametersAsync() - end");
}
protected override void OnInitialized()
{
Log("Child-OnInitialized()");
}
protected override async Task OnInitializedAsync()
{
Log("Child-OnInitializedAsync() - start");
if (UseAwaitInOnInitializedAsync)
{
await Task.Delay(1);
}
Log("Child-OnInitializedAsync() - end");
}
protected override void OnParametersSet()
{
Log($"Child-OnParametersSet() - Value={Value}");
}
protected override async Task OnParametersSetAsync()
{
Log("Child-OnParametersSetAsync() - start");
if (UseAwaitInOnParametersSetAsync)
{
await Task.Delay(1);
}
Log("Child-OnParametersSetAsync() - end");
}
protected override bool ShouldRender()
{
Log("Child-ShouldRender()");
if (!EnableShouldRenderFilter)
{
return true;
}
return Value % 2 == 0; // chỉ render khi Value chẵn
}
protected override void OnAfterRender(bool firstRender)
{
Log($"Child-OnAfterRender(firstRender: {firstRender})");
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
Log($"Child-OnAfterRenderAsync(firstRender: {firstRender}) - start");
if (UseAwaitInOnAfterRenderAsync)
{
await Task.Delay(1);
}
Log($"Child-OnAfterRenderAsync(firstRender: {firstRender}) - end");
if (_clearLogsBeforeNextRender)
{
_logs.Clear();
_displayLogs.Clear();
_clearLogsBeforeNextRender = false;
}
}
public void Dispose()
{
Console.WriteLine($"Child-Dispose() {DateTime.Now:HH:mm:ss.fff}");
}
private void Log(string msg)
=> _logs.Add($"{DateTime.Now:HH:mm:ss.fff} - {msg}");
}