Жизненный цикл компонента Razor

Все компоненты Razor, с которыми мы работали ранее, наследуются от базового абстрактного класса ComponentBase. У этого класса определены методы, благодаря которым мы можем управлять жизненным циклом компонента Razor.

Методы жизненного цикла компонента Razor

У класса ComponentBase определены следующие методы жизненного цикла:

  • OnInitialized() и OnInitializedAsync()
  • OnParametersSet() и OnParametersSetAsync()
  • OnAfterRender(bool firstRender) и OnAfterRenderAsync(bool firstRender)

при этом синхронные версии методов вызываются перед асинхронными. Рассмотрим эти методы более подробно.

OnInitialized() и OnInitializedAsync()

Методы OnInitialized() и OnInitializedAsync() используются исключительно для инициализации компонента на протяжении всего срока службы экземпляра. Используя эти методы, можно, например, инициализировать значения каких-либо свойств или полей компонента. Если используется синхронная версия метода, то инициализация родительского компонента гарантировано будет завершена раньше, чем дочернего. В случае использования асинхронных версий предугадать какой компонент будет инициализирован первым невозможно, так как очередность инициализации будет зависеть от кода метода инициализации.

Чтобы продемонстрировать вышесказанное, создадим новое приложение Blazor Hybrid и изменим код компонентов Home и Counter следующим образом:

Counter

@page "/counter"

<h1>@message</h1>

<p role="status">Current count: @CurrentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    string message = "";

    [Parameter]
    public int CurrentCount { get; set; } = 0;

    protected override void OnInitialized()
    {
        message = $"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}";
    }


    private void IncrementCount()
    {
        CurrentCount++;
    }
}

Home

@page "/"

<h1>@message</h1>

Welcome to your new app.
<Counter CurrentCount="10"></Counter>

@code{
    string message = "";

    protected override void OnInitialized()
    {
        message = $"Компонент Home инициализирован {DateTime.Now:hh:mm:ss:fff}";
    }
}

Теперь Counter является дочерним компонентом для Home. У обоих компонентов мы переопределили синхронный метод OnInitialized() в которых выводим точное время выполнения метода. Запустим приложение и посмотрим на результат:

Сколько бы раз мы не перезапускали приложение компонент Counter будет инициализирован всегда позже, чем Home. Теперь попробуем применить асинхронные версии методов следующим образом:

Метод OnInitializedAsync() компонента Counter

protected override async Task OnInitializedAsync()
{
    await Task.Delay(Random.Shared.Next(100,1000));
    message = $"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}";
}

Метод OnInitializedAsync() компонента Home

protected override async Task OnInitializedAsync()
{
    await Task.Delay(Random.Shared.Next(100,1000));
    message = $"Компонент Home инициализирован {DateTime.Now:hh:mm:ss:fff}";
}

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

Как видно по рисунку, родительский компонент инициализирован позже дочернего.

OnParametersSet() и OnParametersSetAsync()

Эти методы вызываются после инициализации компонента в OnInitialized() или OnInitializedAsync() и после того, как родительский компонент устанавливает параметры дочернего компонента. Чтобы продемонстрировать очередность выполнения методов, снова изменим компоненты Home и Counter следующим образом:

Counter

@page "/counter"

<ul>
    @foreach (string s in message)
    {
        <li>@s</li>
    }
</ul>


<p role="status">Current count: @CurrentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    List<string> message = [];

    [Parameter]
    public int CurrentCount { get; set; } = 0;

    protected override void OnInitialized()
    {
        message.Add($"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}");
    }

    protected override void OnParametersSet()
    {
        message.Add($"Параметры компонента Counter установлены {DateTime.Now:hh:mm:ss:fff}");
    }

    private void IncrementCount()
    {
        CurrentCount++;
    }
}

Home

@page "/"

<h3>
    <ul>
        @foreach (var s in message)
        {
            <li>@s</li>
        }
    </ul>
    
</h3>

Welcome to your new app.
<Counter CurrentCount="10"></Counter>

@code{
    List<string> message = [];

    protected override void OnInitialized()
    {
        message.Add($"Компонент Home инициализирован {DateTime.Now:hh:mm:ss:fff}");
    }

    protected override void OnParametersSet()
    {
        message.Add($"Параметры компонента Home установлены {DateTime.Now:hh:mm:ss:fff}");
    }
}

Так как мы используем синхронные методы, то все методы будут выполняться по порядку — сначала пройдет инициализация Home, затем сработает метод OnParametersSet(), затем инициализируется Counter и ему будут назначены параметры:

OnAfterRender(bool firstRender) и OnAfterRenderAsync(bool firstRender)

Методы OnAfterRender() и OnAfterRenderAsync() вызываются после интерактивной отрисовки компонента и завершения обновления пользовательского интерфейса. В этом методе рекомендуется производить какие-либо дополнительные шаги по инициализации компонента, например, для вызова скриптов JS, которые взаимодействуют с уже отрисованными элементами DOM.

При этом, параметр firstRender устанавливается в значение true при первом рендеринге экземпляра компонента и может быть использован для обеспечения того, чтобы работа по инициализации выполнялась только один раз.

Эти методы вызываются последними. Например, переопределим этот метод в компоненте Counter:

@page "/counter"

<ul>
    @foreach (string s in message)
    {
        <li>@s</li>
    }
</ul>


<p role="status">Current count: @CurrentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    List<string> message = [];

    [Parameter]
    public int CurrentCount { get; set; } = 0;

    protected override void OnInitialized()
    {
        message.Add($"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}");
    }

    protected override void OnParametersSet()
    {
        message.Add($"Параметры компонента Counter установлены {DateTime.Now:hh:mm:ss:fff}");
    }

    private void IncrementCount()
    {
        CurrentCount++;
    }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            message.Add($"Рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}");
        }
        else
        {
            message.Add($"Повторный рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}");
        }
    }
}

Теперь запустим приложение и посмотрим на результат:

Так как компонент уже был отрисован перед выполнением метода OnAfterRender(), то строка «Рендеринг компонента…» была добавлена в список message, но не была выведена на страницу. Чтобы увидеть время рендеринга компонента можно поступить двумя способами:

1. Заставить компонент перерисоваться, например, нажав кнопку «Click me». Результат будет следующим:

2. Вызвать метод StateHasChanged() базового класса, чтобы заставить компонент перерисоваться, например, так:

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        message.Add($"Рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}");
        StateHasChanged();
    }
    else
    {
        message.Add($"Повторный рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}");
    }
}

Теперь строки из метода OnAfterRender() будут показываться сразу после рендеринга:

Метод StateHasChanged() стоит использовать с осторожностью, чтобы не получить бесконечный рендеринг компонента. Например, вот так делать НЕ надо:

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        message.Add($"Рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}");
    }
    else
    {
        message.Add($"Повторный рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}");
    }
    StateHasChanged(); //получаем бесконечный рендеринг
}

Результат будет вот таким:

Итого

Компоненты Razor имеют четко определенный жизненный цикл, воздействовать на который мы можем переопределением методов жизненного цикла. Порядок выполнение методов от инициализации компонента до его удаления из интерфейса следующий: OnInitialized() —> OnParametersSet() —> OnAfterRender(firstRender = true) —> OnAfterRender(firstRender = false) —> ….. OnAfterRender(firstRender = false).

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии