We can easily share state data between components (e.g. a page and a layout), but changing the data from either of them does not automatically re-renders the other if necessary.
-
Create a class that contains the data. The class should have an event which we will use to notify the subscribers (our components).
using System.Collections.ObjectModel; public class NamesService{ private readonly List<string> _names = new List<string>(); public ReadOnlyCollection<string> Names { get { return _names.AsReadOnly(); } } //the event public event Action? StateChanged; public void AddName(string name) { _names.Add(name); StateChanged?.Invoke(); //raise the event } }
We are using event. Dependents (blazor components) of this class should subscribe to
StateChanged
event.Note that we are returning a readonly collection to avoid adding data directly, dependents should use
AddName
instead to raise the event. -
Register the state container in DI as a Singleton. In your
Program.cs
.
builder.Services.AddSingleton<NamesService>();
Since we do not want to create a new instance of
NamesService
everytime we switch the page, we will make it a singleton. -
Subscribe to
StateChanged
event from our component. In our layout for example:
@inherits LayoutComponentBase @implements IDisposable @inject NamesService NamesService <!-- some code goes here --> <span>@NamesState.Names.Count</span> <button @onclick="Add">Add</button> <!-- some code goes here --> @Body <!-- some code goes here --> @code { protected override void OnInitialized() { NamesService.StateChanged += this.StateHasChanged; // subscribe } protected void Add() { NamesService.Add("from layout: " + DateTime.UtcNow.ToString()); // add random data } public void Dispose() { NamesService.StateChanged -= this.StateHasChanged; // unsubscribe } }
Things to note:
- We use @inject to get the instance of the
NamesService
. - When the component is initialized (
OnInitialized
), we subscribe toNamesService.StateChanged
with the component'sStateHasChanged
method. - We implement
IDisposable
, so that we can unsubscribe fromNamesService.StateChanged
when the page is disposed.
In our page, it is the same:
@page "/test" @implements IDisposable @inject NamesService NamesService <ul>@foreach(var name in NamesState.Names) { <li>@name</li> } </ul> <button @onclick="Add">Add</button> @code { protected override void OnInitialized() { NamesService.StateChanged += this.StateHasChanged; // subscribe } protected void Add() { NamesService.Add("from page: " + DateTime.UtcNow.ToString()); // add random data } public void Dispose() { NamesService.StateChanged -= this.StateHasChanged; // unsubscribe } }
- We use @inject to get the instance of the
To summarize, when the NamesService.Add()
is called, it raises the event NamesService.StateChanged
, all subscribers of the event will be notified, in our example, StateHasChanged
of our components will be called.
Our components can now add data and the changes should reflect to all subscribing components.
Resources
Microsoft Docs - Event
Microsoft Docs - Service Lifetime
Microsoft Docs - StateHasChanged
Microsoft Docs - State Management
Top comments (0)