I want to figure out what's the best approach to send re-render requests to a component when an application wide class' (AppState pattern) property has changed.
Following this nice blog post from Chris Sainty, the third example is exactly the kind of thing I want to achieve.
Have a singleton/scoped service, AppState, with the main purpose to store session data to be consumed by several components, invoking an event action when one of it's properties changes, after getting some data from an external api for example. The component can then listen to that and call StateHasChanged()
to re-render the page.
This works but I have a couple of questions about the method.
Is it the best approach for doing this or are there more appropriate ways implemented into the language by default? Either for the event based communication or the whole AppState service. I'd imagine this is quite a common problem and perhaps I'm missing some default functionality like
@bind
.Are there any drawbacks to
@implements IDisposable
? I understand why it needs to be disposed of, but why don't we need to do it when, for instance, binding some data to an input field to essentially do the same thing? Does the@bind
attribute already handle that under the hood?
Thanks in advance and sorry if I'm missing something obvious, still learning the ropes of Blazor.
CodePudding user response:
Is it the best approach for doing this or are there more appropriate ways implemented into the language by default?
For Communication between unrelated components, we have different options:
AppState
registered as Scoped/Singleton. (extra overhead of registering and disposing event).AppState
asrazor
file (code example is given below).- Follow
MVVM
pattern for storingstate
, but there is downside to this approach we need to inject everyview model
asScoped/Singleton
. (If we are re using same component multiple times then each component state will be same).
Based on the requirement you can choose the best.
Example for AppState as razor file (preserving state of counter)
App.razor
will look like this:
<AppState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</AppState>
and AppState.razor
will look like this:
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
@code {
private int _count;
[Parameter]
public required RenderFragment ChildContent { get; set; }//use 'required' only if .net version is 7 & above
public int Count
{
get => _count;
set
{
_count = value;
StateHasChanged();
}
}
}
and Counter.razor will look like this:
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @State.Count</p>
<button @onclick="IncrementCount">Click me</button>
@code {
[CascadingParameter]
public required AppState State { get; set; }//use 'required' only if .net version is 7 & above
private void IncrementCount()
{
State.Count ;
}
}
Are there any drawbacks to @implements IDisposable?
No, only an extra overhead, we need to dispose all unmanged resource manually.