Когда мне следует вызвать StateHasChanged и когда Blazor автоматически перехватывает что-то изменилось? - PullRequest
3 голосов
/ 06 февраля 2020

Мне трудно понять, когда я должен позвонить StateHasChanged() и когда Blazor перехватывает, что что-то изменилось, поэтому оно должно быть повторно обработано.

Я создал пример проекта с кнопкой и пользовательский компонент под названием AddItem. Этот компонент содержит div с красной рамкой и кнопку.

То, что я ожидал: Я хочу, чтобы div AddItem отображался, когда пользователь нажимает кнопку, содержащуюся на странице Index. , Тогда я хочу скрыть это, когда пользователь нажимает кнопку AddItem.

Примечание: AddItem не раскрывает его _isVisible флаг снаружи, вместо этого он содержит метод Show(). Таким образом, AddItems.Show() будет вызываться при нажатии кнопки индекса.

Тесты:

  1. Я нажимаю на кнопку индекса индекса, а затем методы Open() и AddItem.Show(). Флаг _isVisible установлен на true, но ничего не происходит, и индекс ShouldRender() вызывается.

    Вывод на консоль:

    • Индекс визуализации
  2. Я изменил AddItem.Show() с public void Show() {_isVisible = true; StateHasChanged();}. Теперь div AddItem показывает и скрывает, как и ожидалось.

    Вывод на консоль:

    • Визуализация AddItem (1 ° нажатие на кнопку индекса)
    • Визуализация индекса (1 ° нажатие) на кнопку индекса)
    • Визуализация AddItem (2 ° нажмите на кнопку закрытия addItem)
  3. Я изменил <AddItem @ref="AddItem" /> с <AddItem @ref="AddItem" CloseEventCallback="CallBack" />, удален StateHasChanged из метода AddItem Show(). Теперь div AddItem показывает и скрывает, как и ожидалось.

На основании теста 3: Почему мне не нужно явно указывать StateHasChanged, если я установил AddItem's CloseEventCallback для любого родительского метода? Мне трудно это понять, потому что AddItem нигде не вызывает CloseEventCallback.

и

Когда Blazor понимает, что что-то изменилось, так что это должно быть re-render?

Мой пример кода (если вы хотите попробовать его).

Мой Index.razor

<AddItem @ref="AddItem" />
<button @onclick="Open">click</button>
@code {
    AddItem AddItem;

    public void Open()
    {
        AddItem.Show();
    }

    public void CallBack()
    {
    }

    protected override bool ShouldRender()
    {
        Console.WriteLine("Render INDEX");
        return base.ShouldRender();
    }
}

Мой компонент AddItem

@if (_visible)
{
    <div style="width: 100px; height: 100px; border: 1px solid red">testo</div>
    <button @onclick="Close">close</button>    
}

@code {
    private bool _visible = false;

    [Parameter] public EventCallback<bool> CloseEventCallback { get; set; }

    public void Show()
    {
        _visible = true;
    }

    public void Close()
    {
        _visible = false;
    }

    protected override bool ShouldRender()
    {
        Console.WriteLine("Render ADDITEM");
        return base.ShouldRender();
    }
}

1 Ответ

2 голосов
/ 06 февраля 2020

Вообще говоря, метод StateHasChanged () автоматически вызывается после срабатывания события пользовательского интерфейса, например, после нажатия элемента кнопки возникает событие click и автоматически вызывается метод StateHasChanged () для уведомления компонента что его состояние изменилось, и он должен выполнить повторную визуализацию.

При первоначальном доступе к компоненту Index. Сначала выполняется родительский компонент, а затем родительский компонент - его дочерний.

При каждом нажатии кнопки «Открыть» компонент индекса повторно выполняет визуализацию (это потому, что целью события является родительский компонент, который по умолчанию будет выполнен повторный рендеринг (не нужно использовать StateHasChanged). Но не ребенок, который не знает, что его состояние изменилось. Чтобы ребенок знал, что его состояние изменилось и что он должен повторно выполнить рендеринг, вы следует добавить вызов метода StateHasChanged вручную в методе Show. Теперь, когда вы нажимаете кнопку «Открыть», дочерний компонент сначала перерисовывается, а затем перерисовывается его родительский компонент. Теперь отображается красный div visible.

Нажмите кнопку «Закрыть», чтобы скрыть красный div. На этот раз только дочерний компонент выполняет повторный рендеринг (это связано с тем, что целью события является дочерний компонент, и по умолчанию он повторяется. ), но не родитель.

Это поведение является правильным и умышленным.

Если вы удалите вызов метод StateHasChanged из метода AddItem.Show, определите это свойство: [Parameter] public EventCallback<bool> CloseEventCallback { get; set; } и добавьте атрибут компонента в родительский компонент, чтобы присвоить значение этому свойству следующим образом: <AddItem @ref="AddItem" CloseEventCallback="CallBack" />, вы не заметите никаких внешних изменений, но на этот раз порядок повторного рендеринга при нажатии кнопки «Открыть» - сначала родительский рендеринг, затем дочерний рендеринг. Это точно описывает проблему, которую вы обнаружили в своем вопросе в комментариях:

Итак, почему мой тест 3 сработал так, как ожидалось, даже если CloseEventCallback нигде не вызывался?

Вы правы ... Я действительно не мог объяснить это поведение до дальнейшего расследования. Я постараюсь выяснить, что происходит, и дам вам знать.

Метод close AddItem вызывает метод CloseEventCallback, чтобы сообщить родительскому элементу о необходимости повторного рендеринга.

Примечание: ваш код определяет CloseEventCallback с указателем логического типа, поэтому вы должен определить метод в родительском компоненте, который имеет логический параметр. Когда вы вызываете «делегат» CloseEventCallback, вы фактически вызываете метод Index.Callback, и вам следует передать ему логическое значение. Естественно, если вы передаете значение компоненту, вы ожидаете его повторного рендеринга, чтобы новое состояние можно было увидеть в пользовательском интерфейсе. И это та функциональность, которую обеспечивает EventCallback: хотя событие запускается в дочернем компоненте, его целью является родительский компонент, что приводит к повторной визуализации родительского компонента.

Мне интересно, почему родительский компонент должен заново визуализировать себя, если вызван один из подписанных EventCallback?

Это именно то, что я пытаюсь объяснить в параграфе выше. Тип EventCallback был специально разработан для решения проблемы цели события, перенаправления события на компонент, состояние которого изменилось (родительский компонент), и его повторной визуализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...