За долгое время работы с Blazor, начиная с самых ранних сборок и заканчивая .NET Core 3.1.0 мне пришлось достаточно детально рассмотреть процесс взаимодействия с этим фреймом.
И наверно самым главным элементом в данной тематике можно выделить лайфтайм компонента ComponentBase. Если вы хоть раз заглядывали в этот абстрактный интерфейс, то вы наверняка знаете про три метода и три асинхронные перегрузки этих методов. (OnInitialized, OnParametersSet, OnAfterRender).
Не заглядывая в документацию можно с легкостью ответить, за что ответственен каждый из этих методов.
Первый вызывается сразу же, как только пользователь обратился к конкретной странице с указанным компонентом. Класс отвечающий за рендер сразу же вызывает этот метод у корневых компонентов. На данном этапе никто не гарантирует, что компонент получил все параметры (проперти с аттрибутом [Parameter]).
После того, как в компоненте завершилась инициализация, сразу же происходит вызов второго по счету метода, OnParametersSet. В описании метода четко написано, что данный метод вызывается, как только компонент получил все параметры от его родительского компонента.
После того, как на корневом элементе успешно завершена инициализация с присвоением всех параметров, затем происходит инициализация вложенного компонента. В этом компоненте сразу же начинается инициализация параметров и параллельно этому вызывается метод OnInitalized(). После успешного присвоения всех параметров, в компоненте также вызывается метод OnParametersSet().
Как видно из скриншота исследования лайфтайма (https://git.defend.pw/defend/blazor-lifetime), далее происходит первый рендер наших синхронных компонентов. Этот рендер всегда вызывается автоматически, как только все компоненты завершили свои процессы инициализации.
По сути, до вызова метода происходит опрос востребованных переменных и воссоздание первого DOM-дерева. И только после того, как данные были отправлены на клиент, происходит вызов OnAfterRender(true).
Все дальнейшие вызовы OnAfterRender будут происходить с флагом false, вне зависимости, отрабатывает ли у вас биндинг или вы сами вызываете StateHasChanged(), до тех пор, пока компонент не будет высвобожден.
![blazor-lifetime
CD
blazor-lifetime
Home
Counter
Fetch data
A Not secure
heps: localhost:5001/counter
Counter
Current count: 0
Click me
First Component Start
Second Level Component
Parameter Value: 123
First Component End
blazor- lifetime
info :
info :
info :
info :
info :
Microsoft . Hosting. Lifetime [O]
Now listening on:
https : / / localhost : 5001
Microsoft . Hosting. Lifetime [O]
Now listening on:
http://localhost : 5000
Microsoft . Hosting. Lifetime [O]
Application started. Press Ctrl+C to shut down.
Microsoft . Hosting. Lifetime [O]
Hosting environment: Development
Microsoft . Hosting. Lifetime [O]
Content root path: C:
FirstLeve1Component . Onlni tialized. Start
F i rs tLeve IComponent . Onlni t ia 1 i zed. End
FirstLeve1Component . OnParametersSet . Start
FirstLeve1Component . OnParametersSet . End
SecondLeve1Component . Parameter. set
SecondLeve1Component . Onlni tialized. Start
SecondLeve IComponent . Onlni t i a 1 i zed. End
SecondLeve1Component . OnParametersSet . Start
SecondLeve1Component . OnParametersSet . End
SecondLeve1Component . Parameter. get
FirstLeve1Component . OnAfterRender (FirstRender) . Start
FirstLeve1Component . OnAfterRender (FirstRender) . End
SecondLeve1Component . OnAfterRender (FirstRender) . Start
SecondLeve1Component . OnAfterRender (FirstRender) . End](https://defend.pw/wp-content/uploads/2020/02/image-1024x540.png)
Сейчас кто-то может отметить, что сейчас были описаны практически очевидные вещи и он будет абсолютно прав. Здесь у меня нет никаких вопросов к логике лайфтайма. Все загвоздки начнутся далее.
Давайте теперь рассмотрим аналогичный код и для асинхронных версий методов. Ведь вы в любом случае столкнетесь с необходимостью асинхронного обращения к JsInvoke для вызова js-метода (в данном случае нельзя синхронно вызывать js-скрипт). Или же необходимо ввести какую-нибудь задержку. Обратиться асинхронно к базе данных.
Кейсов асинхронного взаимодействия — куча.
В этом случае я буду использовать практически такой же код, но в качестве задержек буду использовать await Task.Delay().

И вот ровно с этого момента начинается ад. Вне зависимости от конфигурации сервера, эта цепочка ведет себя именно так. Происходит инициализация первого компонента, затем сразу же второго, затем сразу же происходит попытка рендера.
Затем у каждого компонента в отдельности происходит вызов OnParameterSet. И хочется отметить самое важное. У второго компонента вызов этого метода уже произошел, однако значение внутреннего параметра еще не было установлено. Хотя для этого метода четко обозначено то же самое определение.
Попытка накатать issue в официальный репозиторий ASP.NET не дала результатов. Команда разработчиков Blazor считает, что данный фреймворк работает так как надо. Однако в асинхронном кейсе, при использовании данного лайфтайма, ты никогда не будешь уверен в том, что все параметры внешнего компонента будут адекватно проинициализированы.
Да, в моем приведенном случае компонент успешно перерендерил значение null в value. Но это произошло успешно, потому что я предварительно сделал проверку на null.
В следующей части статьи я приведу пример решения (костыля), чтобы использовать асинхронные кейсы на лету, не боясь получить NullReferenceException, а также расскажу еще несколько деталей о компонентах в Blazor, которые существенно повлияют на подход к разработке в данной среде, в целом.