Лучший способ запустить повторный рендеринг в Blazor после изменения данных

У меня есть приложение Blazor Server с основным компонентом макета, который содержит несколько дочерних компонентов. Каждый компонент регистрируется для события, определенного в статическом классе. Когда пользователь нажимает кнопку в Child1 (например), он изменяет некоторые данные в классе обслуживания, а затем вызывает статический класс, чтобы вызвать событие, которое затем вызывает повторную визуализацию Child2 (используя InvokeAsync(StateHasChanged) в своем обработчике событий). ), заставляя Child2 отражать новые данные. Другой подход заключается в том, чтобы каждый компонент регистрировался для события, которое определяется и инициируется службой данных, как описано в этом примере.

Есть ли здесь какая-либо причина отдавать предпочтение событию службы данных, а не статическому классу? Любой из них, кажется, работает, хотя, возможно, основанный на сервисе вариант немного более элегантный/жесткий. С другой стороны, версия статического класса может использоваться для внесения изменений в несколько служб данных.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
135
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Рассмотрите область действия вашего объекта отслеживания состояния и вашего компонента/страницы. В идеале они должны совпадать.

Ваш статический класс этого не делает. Он доступен всем пользователям и всем экземплярам страницы.

Мой первоначальный ответ, который вы здесь процитировали, тоже этого не делает. Это то, над чем я работаю какое-то время. DotNetCore не предоставляет нам DI-контейнер, который мы можем применить к компоненту/странице. OwningComponentBase признает проблему и пытается ее решить, но не подходит для этой цели.

Рассмотрим следующую реализацию страницы счетчика.

Объект состояния счетчика:

public class CounterState : IDisposable
{
    // This is here simply to demonstrate Dependancy Injection
    private NavigationManager _navigationManager;
    
    public int Counter { get; private set; }

    public event EventHandler<CounterChangedEventArgs>? CounterChanged;

    public CounterState(NavigationManager navigationManager)
        => _navigationManager = navigationManager;

    public void IncrementCounter(object? sender)
    {
        this.Counter++;
        this.CounterChanged?.Invoke(sender, CounterChangedEventArgs.Create(this.Counter));
    }

    // implementated to demonstrate dealing with an IDisposable State object
    public void Dispose() { }
}

И обычай EventArgs

public class CounterChangedEventArgs : EventArgs
{
    public int CounterValue { get; set; }

    public static CounterChangedEventArgs Create(int value)
        => new CounterChangedEventArgs {  CounterValue = value };
}

Простой компонент отображения счетчика:

@implements IDisposable

<div class = "bg-dark text-white m-2 p-2">
    <pre>Value : @_counterState?.Counter </pre>
</div>

@code {
    [CascadingParameter] private CounterState _counterState { get; set; } = default!;

    protected override void OnInitialized()
    {
        ArgumentNullException.ThrowIfNull(_counterState);
        _counterState.CounterChanged += this.OnCounterChanged;
    }

    private void OnCounterChanged(object? sender, CounterChangedEventArgs e)
        => this.InvokeAsync(StateHasChanged);

    public void Dispose()
        => _counterState.CounterChanged -= this.OnCounterChanged;
}

И компонент Incrementer:

<div class = "m-2 p-2">
    <button class = "btn btn-primary" @onclick=this.OnIncrementCounter>Increment</button>
</div>

@code {
    [CascadingParameter] private CounterState? _counterState { get; set; }

    protected override void OnInitialized()
    =>  ArgumentNullException.ThrowIfNull(_counterState);

    private Task OnIncrementCounter()
    {
        _counterState?.IncrementCounter(this);
        return Task.CompletedTask;
    }
}

И, наконец, обновленная страница Counter:

@page "/counter"
@implements IDisposable
@inject IServiceProvider serviceProvider

<PageTitle>Counter</PageTitle>

<h1>Counter Page</h1>
<CascadingValue Value = "_counterState">
    <CounterViewer />
    <CounterViewer />
    <CounterViewer />
    <CounterViewer />
    <CounterViewer />
    <div class = "row">
        <div class = "col-2">
            <CounterButton />
        </div>
        <div class = "col-2">
            <CounterButton />
        </div>
        <div class = "col-2">
            <CounterButton />
        </div>
        <div class = "col-2">
            <CounterButton />
        </div>
    </div>
</CascadingValue>

@code {
    private CounterState? _counterState;

    protected override void OnInitialized()
    {
        // demonstrates creating an object instance in the context of thw Service Container
        // this will inject any defined dependencies  (in our case the Scoped NavigationManager)
        _counterState = ActivatorUtilities.CreateInstance<CounterState>(serviceProvider);
        ArgumentNullException.ThrowIfNull(_counterState);
    }

    // This will get called by the Renderer when the page/component goes out of scope
    public void Dispose()
        => _counterState?.Dispose();
}

Эта реализация:

  1. Создает объект для отслеживания состояния и инициирования событий для уведомления об изменениях состояния.
  2. Объект имеет ту же область действия, что и страница.
  3. Страница каскадирует объект состояния.
  4. Подкомпоненты фиксируют каскадное состояние и вызывают методы для обновления состояния и/или регистрации обработчиков событий для реагирования на изменения состояния.
  5. Страница создает объект состояния в контексте контейнера внедрения зависимостей, поэтому внедряются все необходимые службы внедрения зависимостей.

Важной особенностью этого шаблона является сопоставление области действия объекта состояния с его компонентом-владельцем. Использование DI: Scoped слишком широкое и Transient слишком узкое [каждый компонент получает новый экземпляр]. Если вы каскадируете полученную службу Transient, которая реализует IDisposable, вы создаете утечку памяти. Создание экземпляра с помощью ActivatorUtilities решает проблемы с внедрением зависимостей. Однако вы несете ответственность за реализацию утилизации.

Здесь есть статья, в которой эта тема рассматривается более подробно — https://www.codeproject.com/Articles/5352916/Matching-Services-with-the-Blazor-Component-Scope.

Большое спасибо за ваше невероятно подробное объяснение. Я новичок в Blazor Server (и в веб-программировании в целом), и у меня крутая кривая обучения, связанная с управлением состоянием. Лампочка загорается благодаря вашему ответу. Я думаю, что теперь я «понял», но мне нужно больше опыта, чтобы быть уверенным. В любом случае, вы, безусловно, ответили на мой вопрос и указали, что то, что я делаю, нежизнеспособно.

Steve Rehling 06.04.2023 23:23

Другие вопросы по теме