За долгое время работы с 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(), до тех пор, пока компонент не будет высвобожден.
Сейчас кто-то может отметить, что сейчас были описаны практически очевидные вещи и он будет абсолютно прав. Здесь у меня нет никаких вопросов к логике лайфтайма. Все загвоздки начнутся далее.
Давайте теперь рассмотрим аналогичный код и для асинхронных версий методов. Ведь вы в любом случае столкнетесь с необходимостью асинхронного обращения к JsInvoke для вызова js-метода (в данном случае нельзя синхронно вызывать js-скрипт). Или же необходимо ввести какую-нибудь задержку. Обратиться асинхронно к базе данных.
Кейсов асинхронного взаимодействия — куча.
В этом случае я буду использовать практически такой же код, но в качестве задержек буду использовать await Task.Delay().
И вот ровно с этого момента начинается ад. Вне зависимости от конфигурации сервера, эта цепочка ведет себя именно так. Происходит инициализация первого компонента, затем сразу же второго, затем сразу же происходит попытка рендера.
Затем у каждого компонента в отдельности происходит вызов OnParameterSet. И хочется отметить самое важное. У второго компонента вызов этого метода уже произошел, однако значение внутреннего параметра еще не было установлено. Хотя для этого метода четко обозначено то же самое определение.
Попытка накатать issue в официальный репозиторий ASP.NET не дала результатов. Команда разработчиков Blazor считает, что данный фреймворк работает так как надо. Однако в асинхронном кейсе, при использовании данного лайфтайма, ты никогда не будешь уверен в том, что все параметры внешнего компонента будут адекватно проинициализированы.
Да, в моем приведенном случае компонент успешно перерендерил значение null в value. Но это произошло успешно, потому что я предварительно сделал проверку на null.
В следующей части статьи я приведу пример решения (костыля), чтобы использовать асинхронные кейсы на лету, не боясь получить NullReferenceException, а также расскажу еще несколько деталей о компонентах в Blazor, которые существенно повлияют на подход к разработке в данной среде, в целом.