Home > Back-end >  Communication between injected class service (AppState) and components
Communication between injected class service (AppState) and components

Time:11-19

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.

  1. 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.

  2. 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:

  1. AppState registered as Scoped/Singleton. (extra overhead of registering and disposing event).
  2. AppState as razor file (code example is given below).
  3. Follow MVVM pattern for storing state, but there is downside to this approach we need to inject every view model as Scoped/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.

  • Related