إدارة الحالة في Blazor
أديروا الحالة المشتركة باستخدام مفردات الخدمات أو Fluxor أو AppState متسلسل للحفاظ على اتساق واجهة المستخدم بين المكوّنات.
إدارة الحالة في Blazor درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.
تحديات إدارة الحالة في Blazor
مع نمو تطبيقات Blazor، تصبح إدارة الحالة التي يجب مشاركتها بين العديد من المكوّنات غير المرتبطة معقدة. يفقد تمرير الخصائص عبر سلسلة طويلة من المكوّنات فعاليته؛ وتحتاج إلى مخزن حالة مركزي وتفاعلي.
حالة بسيطة باستخدام خدمة ذات نطاق محدد
أبسط أسلوب لإدارة الحالة في Blazor هو استخدام خدمة ذات نطاق محدد تحتفظ بالحالة وتعرّض حدثًا يتيح للمكوّنات الاشتراك في التغييرات.
public class CartState
{
private readonly List<CartItem> _items = new();
public IReadOnlyList<CartItem> Items => _items;
public int Count => _items.Sum(i => i.Quantity);
public event Action? OnChanged;
public void Add(Product p)
{
var existing = _items.FirstOrDefault(i => i.ProductId == p.Id);
if (existing is not null) existing.Quantity++;
else _items.Add(new CartItem { ProductId = p.Id, Name = p.Name, Price = p.Price });
OnChanged?.Invoke();
}
}الاشتراك في تغييرات الحالة
تشترك المكوّنات في حدث OnChanged الخاص بخدمة الحالة، وتستدعي StateHasChanged لإعادة العرض. تذكّر إلغاء الاشتراك في Dispose.
@inject CartState Cart
@implements IDisposable
<p>Cart: @Cart.Count items</p>
@code {
protected override void OnInitialized()
{
Cart.OnChanged += OnCartChanged;
}
private void OnCartChanged()
{
InvokeAsync(StateHasChanged);
}
public void Dispose() =>
Cart.OnChanged -= OnCartChanged;
}استخدام CascadingValue كحاوية للحالة
غلّف التخطيط الجذر في CascadingValue مع خدمة الحالة الخاصة بك. ويمكن لجميع العناصر المنحدرة منه الوصول إليها دون تمرير الخصائص صراحةً عبر المكوّنات.
@* MainLayout.razor *@
@inject CartState Cart
<CascadingValue Value="Cart">
@Body
</CascadingValue>
@* Any nested component: *@
@code {
[CascadingParameter]
public CartState Cart { get; set; } = default!;
private void Buy(Product p) => Cart.Add(p);
}Fluxor: نمط Flux في Blazor
يضيف Fluxor تدفق بيانات أحادي الاتجاه على نمط Redux إلى Blazor: الحالة → المكوّن → الإجراء → المخفِّض → الحالة. وهو مناسب جدًا للتطبيقات المعقدة.
// dotnet add package Fluxor.Blazor.Web
// State record
[FeatureState]
public record CounterState(int Count = 0);
// Actions
public record IncrementAction(int Amount = 1);
// Reducers
public static class CounterReducers
{
[ReducerMethod]
public static CounterState Reduce(CounterState state, IncrementAction action)
=> state with { Count = state.Count + action.Amount };
}
// Registration:
builder.Services.AddFluxor(opt =>
opt.ScanAssemblies(typeof(Program).Assembly));استخدام Fluxor في مكوّن
احقن IState<T> لقراءة الحالة، وIDispatcher لإرسال الإجراءات. يتولى InheritsFrom<FluxorComponent> (أو الـ mixin) الاشتراك تلقائيًا في تغييرات الحالة.
@inherits FluxorComponent
@inject IState<CounterState> CounterState
@inject IDispatcher Dispatcher
<p>Count: @CounterState.Value.Count</p>
<button @onclick="Increment">+1</button>
<button @onclick="() => Dispatcher.Dispatch(new IncrementAction(5))">+5</button>
@code {
private void Increment()
=> Dispatcher.Dispatch(new IncrementAction(1));
}استمرارية الحالة في LocalStorage
احتفظ بالحالة عبر عمليات تحديث الصفحة باستخدام localStorage في المتصفح من خلال التشغيل التفاعلي مع JavaScript. واستعدها عند بدء تشغيل التطبيق.
@inject IJSRuntime JS
@code {
private async Task SaveStateAsync()
{
var json = JsonSerializer.Serialize(_state);
await JS.InvokeVoidAsync("localStorage.setItem", "appState", json);
}
protected override async Task OnInitializedAsync()
{
var json = await JS.InvokeAsync<string?>("localStorage.getItem", "appState");
if (json is not null)
_state = JsonSerializer.Deserialize<AppState>(json) ?? new();
}
private AppState _state = new();
}حزمة Blazored.LocalStorage
توفر حزمة Blazored.LocalStorage واجهة برمجية نظيفة ومحددة الأنواع فوق localStorage في المتصفح، دون كتابة استدعاءات التشغيل التفاعلي مع JavaScript يدويًا.
// dotnet add package Blazored.LocalStorage
builder.Services.AddBlazoredLocalStorage();
@inject ILocalStorageService LocalStorage
@code {
protected override async Task OnInitializedAsync()
{
_cart = await LocalStorage.GetItemAsync<List<CartItem>>("cart") ?? new();
}
private async Task SaveCartAsync()
{
await LocalStorage.SetItemAsync("cart", _cart);
}
private List<CartItem> _cart = new();
}حالة عنوان URL باستخدام NavigationManager
استخدم معلمات الاستعلام للاحتفاظ بالحالة التي ينبغي أن تستمر بعد تحديث الصفحة وأن تكون قابلة للحفظ كإشارة مرجعية. اقرأ هذه المعلمات واكتبها باستخدام NavigationManager ومنشئ URI.
@inject NavigationManager Nav
@page "/products"
@code {
[SupplyParameterFromQuery] public string? Search { get; set; }
[SupplyParameterFromQuery] public int Page { get; set; } = 1;
private void UpdateSearch(string s)
{
Nav.NavigateTo(
Nav.GetUriWithQueryParameters(
new Dictionary<string, object?>
{
[nameof(Search)] = s,
[nameof(Page)] = 1
}));
}
}من الواقع العملي: حالة سلة التسوق
نمط متكامل: خدمة CartState ذات نطاق محدد، تتضمن أحداثًا، وتُحفظ في localStorage عند حدوث تغيير، ثم تُستعاد عند التحميل.
public class CartState
{
private readonly ILocalStorageService _storage;
private List<CartItem> _items = new();
public event Action? OnChanged;
public CartState(ILocalStorageService storage) => _storage = storage;
public async Task InitAsync()
{
_items = await _storage.GetItemAsync<List<CartItem>>("cart") ?? new();
}
public async Task AddAsync(Product p)
{
var item = _items.FirstOrDefault(i => i.ProductId == p.Id);
if (item is not null) item.Quantity++;
else _items.Add(new CartItem { ProductId = p.Id, Name = p.Name });
await _storage.SetItemAsync("cart", _items);
OnChanged?.Invoke();
}
}تحقق سريع
ما أسلوب إدارة الحالة الأنسب لتطبيق Blazor بسيط ذي حالة مشتركة محدودة؟
مراجعة: إدارة الحالة في Blazor
أهم النقاط:
- خدمة ذات نطاق محدد + حدث Action: أبسط أسلوب للحالة المشتركة، ومناسب لمعظم التطبيقات
- يوفر تغليف الجذر في CascadingValue وصولًا على مستوى التطبيق كله دون تمرير الخصائص عبر سلسلة طويلة من المكوّنات
- Fluxor: تدفق أحادي الاتجاه على نمط Redux لمنطق الحالة المعقد
- احتفظ بالحالة في localStorage باستخدام IJSRuntime أو Blazored.LocalStorage
- حالة عنوان URL (SupplyParameterFromQuery) لحالة واجهة مستخدم قابلة للحفظ والمشاركة
- ألغِ الاشتراك دائمًا في الأحداث داخل Dispose لمنع تسرب الذاكرة
الأسئلة الشائعة
هل درس «إدارة الحالة في Blazor» مجاني؟
نعم — نص درس «إدارة الحالة في Blazor» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.
ماذا ستتعلم في «إدارة الحالة في Blazor»؟
أديروا الحالة المشتركة باستخدام مفردات الخدمات أو Fluxor أو AppState متسلسل للحفاظ على اتساق واجهة المستخدم بين المكوّنات. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟
لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «إدارة الحالة في Blazor»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟
نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- نموذج مكوّنات Blazor
- ربط البيانات ومعالجة الأحداث
- التواصل بين المكوّنات وDI
- إدارة الحالة في Blazor