KHÔNG GIAN HỌC TẬP / State Management
Danh mục bài học Blazor Server .NET 10
BÀI 07Component và trạng thái

State Management

Giữ một nguồn dữ liệu chung, thông báo khi dữ liệu thay đổi và cập nhật các phần giao diện đang sử dụng nó.

Giải thích, ví dụ & thực hành Tất cả bài học

Các khu vực trên trang cùng đọc một CounterStateService. Khi bạn thay đổi bộ đếm, service phát OnChange để component biết cần cập nhật giao diện.

Thử ngay. Bấm tăng hoặc giảm bộ đếm, rồi đối chiếu khu vực chỉ đọc và bảng log. Chú ý: dữ liệu nằm trong service; giao diện chỉ đọc dữ liệu đó và phản ứng với thông báo thay đổi.

01. Thay đổi dữ liệu chung

Count: 0

LastAction: none

Xem code
Razor
@inject CounterStateService CounterState

<button class="btn btn-primary" @onclick="Increase">+ Increment</button>
<button class="btn btn-warning" @onclick="Decrease">- Decrement</button>
<button class="btn btn-danger" @onclick="ResetState">Reset</button>

<p><b>Count:</b> @CounterState.Count</p>
<p><b>LastAction:</b> @CounterState.LastAction</p>
C#

private void Increase()
{
    CounterState.Increment();
}

private void Decrease()
{
    CounterState.Decrement();
}

private void ResetState()
{
    CounterState.Reset();
}

02. Đọc cùng một nguồn dữ liệu

Shared Count: 0

Shared LastAction: none

Khu vực này không tự thay đổi state, chỉ đọc từ cùng một service DI. Khi khu vực trên bấm nút, phần này cũng cập nhật theo.

Xem code
@inject CounterStateService CounterState

<p><b>Shared Count:</b> @CounterState.Count</p>
<p><b>Shared LastAction:</b> @CounterState.LastAction</p>

03. Theo dõi sự kiện OnChange

Render Count: 0

State Change Count: 0

Logs:
    Xem code
    Razor
    
    <p><b>Render Count:</b> @_renderCount</p>
    <p><b>State Change Count:</b> @_stateChangeCount</p>
    
    <ul>
    @foreach (var item in _logs)
    {
        <li>@item</li>
    }
    </ul>
    
    <button class="btn btn-outline-secondary" @onclick="ClearLogs">
        Clear Logs
    </button>
    
    C#
    
    protected override void OnInitialized()
    {
        CounterState.OnChange += HandleStateChanged;
    }
    
    private void HandleStateChanged()
    {
        _stateChangeCount++;
    
        _logs.Add(
            $"Count = {CounterState.Count}, Action = {CounterState.LastAction}"
        );
    
        InvokeAsync(StateHasChanged);
    }
    
    public void Dispose()
    {
        CounterState.OnChange -= HandleStateChanged;
    }
    
    protected override void OnAfterRender(bool firstRender)
    {
        _renderCount++;
    }
    

    04. Vì sao cần OnChange?

    • Service đổi state không tự làm UI render lại.
    • Service chỉ nên giữ dữ liệu và bắn sự kiện thông báo thay đổi.
    • Component lắng nghe OnChange rồi gọi InvokeAsync(StateHasChanged).
    • Dùng InvokeAsync(StateHasChanged) an toàn hơn vì callback có thể đến từ ngoài UI context.

    05. Mã nguồn của service

    Xem code service
    
    public class CounterStateService
    {
        public int Count { get; private set; } = 0;
        public string LastAction { get; private set; } = "none";
    
        public event Action? OnChange;
    
        public void Increment()
        {
            Count++;
            LastAction = $"Increment at {DateTime.Now:HH:mm:ss}";
            NotifyStateChanged();
        }
    
        public void Decrement()
        {
            Count--;
            LastAction = $"Decrement at {DateTime.Now:HH:mm:ss}";
            NotifyStateChanged();
        }
    
        public void Reset()
        {
            Count = 0;
            LastAction = $"Reset at {DateTime.Now:HH:mm:ss}";
            NotifyStateChanged();
        }
    
        private void NotifyStateChanged()
            => OnChange?.Invoke();
    }
    

    Điểm cần nhớ

    Service giữ dữ liệu, component chịu trách nhiệm cập nhật giao diện. Đăng ký sự kiện khi component bắt đầu sử dụng service và hủy đăng ký trong Dispose khi rời trang.

    Web hosting by Somee.com