Хочу представить перевод довольно интересной статьи Сэма Редмонда, Why We Should Throw Out React and Pick Up Angular. На мой взгляд, статья описывает основные возможности Angular. Она может показаться довольно вызывающей, но постарайтесь отнестись к ней немного с юмором :)
Итак, я уже немного написал о том, почему, как мне кажется, мы должны перестать использовать React. Чтобы подвести итог этой статьи, я перечислю несколько проблем, с которыми я столкнулся в React:
Он популярен в основном от того, что вокруг него много “шумихи”.
Он даёт слишком много свободы. Это приводит к фундаментальным ошибкам на ранней стадии разработки приложения, которые проявляются не сразу.
Использует не оправдано много памяти и не поддаётся оптимизации(not tree-shakable).
Сложность React приложения растёт экспоненциально с ростом размера приложения и это затрудняет его обслуживание.
Нет ничего встроенного (например, обработка форм). По-этому вам нужно написать много кода, что бы это как-то компенсировать или использовать кучу сторонних библиотек.
Обновление вашего приложения до последней версии React часто сопряжено с полным переписыванием этого самого приложения.
Это самые частые проблемы, с которыми я столкнулся, работая с различными проектами на React. В этой статье я хочу поделиться с вами, как Angular решает большую часть вышеперечисленных проблем и позволяет вам сосредоточиться на том, что вы создаёте, а не на том, как вы создаёте своё приложение.
“All aboard the hype train”
Angular также получает изрядное количество хайпа, так что я не могу сказать, что Angular “решает” эту проблему. Однако, я не думаю, что Angular получает такое же количество хайпа, как React. Мне кажется, что в основном это связано с ребрендингом, который сделал Google.. Сначала был AngularJs, который был как дымящаяся куча мусора. Но надо отдать должное Google, они решили полностью отремонтировать AngularJs и превратить его в Angular (или Angular 2), что является гигантским улучшением. Впрочем, это улучшение стоило ему популярности, как мне кажется.
Лично я думаю, что им следовало бы переименовать Angular во что-то другое, потому что AngularJs и Angular совсем не похожи. Единственное сходство - это названия фреймворков и компания, которая их разработала.
Я убеждён, что Angular вполне заслуживает того хайпа, который он получает. Разработчики Angular создали невероятно хорошо продуманный фрэймворк, который обладает отличной производительностью и довольно прост в использовании. В этой статье я расскажу о преимуществах от использования Angular и, вероятно, после прочтения вы согласитесь с тем, что Angular заслужил свой хайп.
Вы получаете то, что вам нужно
Одна из больших разниц между Angular и React в простом факте, что Angular является полноценным фреймворком в противовес обычной библиотеке. Как я уже показал в предыдущей статье, React пытается совместить лучшее из библиотеки и фреймворка, но делает это плохо. В отличие от этого, Angular не пытается быть библиотекой. Он идёт ва-банк, чтобы быть фреймворком и делает это хорошо.
С базовой установкой вы получаете почти всё, что вам нужно, для того, чтобы создать любое приложение без необходимости устанавливать сторонние библиотеки (я уже знаю, что вы подумали, но об это позже). С Angular вы получаете надёжный фрэймворк, который позволяет, а не сдерживает вас. Вы больше не должны тратить уйму времени, чтобы писать кучу сложного кода, чтобы обработать и провалидировать форму, потому что это всё обрабатывается внутри фреймворка.
Кроме того вы получаете такую штуку, как Angular CLI, который один из самых мощных инструментов на поясе у Angular.
Одна из самых больших проблем с React - это отсутствие стандартов. Если вы изучили одно React приложение, то вы изучили одно React приложение, потому что все они совершенно разные. С другой стороны, если вы изучили одно Angular приложение, то вы изучили все Angular приложения и Angular CLI является основным драйвером, стоящим за этим.
В отличие от React в Angular есть правильные и неправильные способы. Использование Angular CLI обычно всегда гарантирует, что всё будете делать правильно. Давайте возьмём самое начало. Мы хотим создать новое приложение. Как мы это сделаем?
ng new my-app
Да, вот, пожалуй и всё. Запустите эту команду и CLI настроит кучу вещей за вас. Он даже даст вам некоторый выбор, такой как использование линтинга и роутинга, перед тем, как будет создано приложение. Итак, вот что сделает CLI:
Он создаст новую директорию my-app и проинициализирует в ней Angular приложение. А также установит зависимости.
Настраивает инфраструктуру и интегрирует в неё приложение, снабдив вас всем, что нужно для запуска.
Вместе с инфраструктурой вы также получаете встроенную базовую реализацию end-to-end тестов на Protractor, в которую вы можете добавлять тесты, по мере развития вашего приложения.
Angular даёт вам простой в использовании конфигурационный файл (angular.json). В нём вы можете легко настроить, как Angular собирает приложение и даже то, как эта сборка делается средой окружения.
Говоря о среде окружения, Angular имеет в своём составе простую и хорошо типизированную систему управления этой самой средой.
Ещё много есть того, чего я возможн коснусь, но и это уже очень круто, да ещё и прямо “из коробки”. В React вы такого не получите. Что ещё даёт вам CLI?
Кроме создания самого приложения и каркаса к нему, он будет также управлять приложением за вас. Скажем, у вас уже есть новенькое приложение и вы хотите создать компонент.
ng generate component my-component OR ng g c my-component
Существует два способа как создать компонент и они довольно долгие, по сравнению с короткой командой, что довольно приятно. Эта команда делает следующее:
Создаёт директорию с именем my-component и помещает в него пустой компонент.
Автоматически генерирует unit tests для данного компонента.
Автоматически встраивает ваш компонент в инфраструктуру приложения.
Путь Angular
Я упомянул ранее о правильных и неправильных способах делать разные вещи, а также я упомянул о том, что происходит вокруг создания инфраструктуры. Но как это выглядит на самом деле? Я не буду приводить подробного руководства, а лишь кратко пройдусь по главным концептам. Некоторые примеры вы сможете найти на stackblitz:
https://stackblitz.com/edit/angular-examples-modules
ng g m button
ng g c button
В этом примере у нас есть папка button. В этой папке есть модуль button, компонент button, тестовый файл, файл стилей и HTML файл.
Это было автоматически сгенерировано Angular CLI. Если вы заметили, команды имеют определённый порядок и на это есть причина. Во-первых нам нужен модуль, чтобы включить в него наш компонент button. Затем мы создаём компонент button и CLI автоматически импортирует его в модуль. Затем мы экспортируем наш компонент для того, чтобы мы могли использовать его в других модулях нашего приложения.
Чтобы всё было просто, давайте импортируем это в app.module.ts. Всё, что мы сделаем, это импортируем наш компонент ButtonModule в app.module.ts и потом включим его в раздел imports декоратора @NgModule приложения AppModule.
Вот и всё. Теперь мы можем использовать тэг <app-button></app-button> в app.component.html файле.
Это самый простой пример реализации модулей в Angular и его можно реализовать другими способами. Но базовая реализация, которую мы здесь увидели, в основном остаётся такой же, независимо от того, как вы её используете. Поэтому, даже если ваш случай отличается от приведённого примера, это не усложнит вам жизнь.
Доставьте меня из пункта А в пункт Б
Я хотел бы коснуться ещё одной фичи, которую предоставляет вам Angular “из коробки”. Это роутинг и ленивая загрузка модулей. Я не буду здесь сильно углубляться и приведу небольшой пример. Если вы хотите подробнее разобрать эти фичи, попробуйте это сделать своими руками в Angular.
Как хороший CLI, Angular имеет опции. Одна из них задействует роутинг во время создания инфраструктуры приложения.
ng new my-app --routing
На сегодняшний день вы можете просто выбрать опцию во время установки, без указания флага. Но флаг тоже можно указывать, если вы так больше предпочитаете. Когда вы выбираете routing, Angular создаёт файл роутинга за вас. Он будет служить вам в качестве основного конфигурационного файл. Если вы захотите создать модули для ваших страниц, вы также можете указать флаг routing. CLI тогда создаст конфиг роутинга для этого модуля.
В созданном конфиге роутинга вы сможете легко настроить ленивую загрузку модуля:
const routes: Routes = [
{
path: 'main',
loadChildren: () => import('src/app/routes/main/main.module').then((mod) => mod.MainModule)
},
];
Если вы прописываете ленивую загрузку, то вы должны использовать синтаксис асинхронного импорта модуля, который хотите загрузить. Это означает, что когда приложение загружается, клиент не должен загружать код для модуля main, каких либо зависимостей или чего-то, что модуль main использует до тех пор, пока пользователю не понадобиться загрузить это. Это сделает начальную загрузку приложения более лёгкой и быстрой и гарантирует, что пользователь не будет ждать, пока не загрузится всё приложение целиком. Самое замечательное, что эта возможность предоставляется из коробки и всё, что вам нужно, это использовать маленький флаг при создании вашего приложения и модулей.
Переиспользование делает вашу жизнь проще
Итак, мы рассмотрели, как Angular использует Modules для организации страниц и компонент и хочу остановиться на этом немного подробнее. Вероятно, что вы уже знаете, что Angular использует TypeScript вместо обычного javascript. Это явное преимущество Angular использует в полной мере.
Полагаю, что вы уже говорите - “React тоже использует TypeScript”, и будете правы. Но React использует Typescript не в полной мере. Типизация - это только часть того, что делает TypeScript таким продвинутым. Другая часть состоит в том, что вы можете в полной мере использовать классы и декораторы. React дал ясно понять, что движется в сторону функциональных компонент, а не в сторону компонент основанных на классах, что отнимает половину всей мощи TypeScript. Теперь стало намного сложнее переиспользовать функциональность в моём приложении и оно стало более сложным в обслуживании.
В Angular я могу создать декоратор, который подключается к методам жизненного цикла, чтобы выполнить набор стандартных операций и всё что мне нужно сделать, это прописать декоратор перед декларацией класса.
@my-decorator()
export class MyClass {}
Это сделает мой код более читаемым и удобным в обслуживании даже при добавлении большей функциональности к тому, что я создаю.
Прежде чем я начну, мне хотелось бы поблагодарить Виктора Савкина за его превосходную статью, из которой я многое почерпнул об Angular Ivy.
Understanding Angular Ivy: Incremental DOM and Virtual DOM
В предыдущей статье я немного рассказывал о том, что виртуальный DOM это своего рода “пожиратель памяти” и что невозможно оптимизировать расход этой памяти (tree shaking), поскольку виртуальное дерево создаётся всякий раз заново при перерисовке. Всё изменилось с приходом Angular Ivy. Вы сможете прочитать как работает Ivy в статье Виктора. Я лишь приведу некоторые моменты из неё.
Angular Ivy использует так называемый “инкрементный DOM”. Идея заключается в том, что каждый компонент ссылается на набор инструкций, которые известны на стадии компиляции. И если некоторые инструкции не используются, то они исключаются из сборки.
В отличие от виртуального DOM, в инкрементном DOM память расходуется при перерисовке дерева DOM, если только в нем произошли изменения (то есть были удалены или добавлены элементы). Кроме того, выделение памяти пропорционально размеру изменений. Это здорово повышает эффективность в рантайме, особенно для мобильных устройств.
Помните, как я ранее сказал: “Я уже знаю, что вы думаете и мы к этому вернёмся”? Давайте разберём это. В том месте статьи я уверен, что вы подумали про себя: “это всё прелестно, но что если мне не понадобиться ВСЁ, что Angular предоставляет”? Хорошо, ну вы сами вдумайтесь, если вы не используете какую-то часть Angular, она просто не попадёт в сборку! Их технология оптимизации сборки постоянно улучшается и вы получите более “стройные” билды, в особенности если вы не используете абсолютно всё, что есть в Angular.
Апгрейд - проще простого
Если вы читаете это, то вероятно вы, вы уже проработали каким-то разработчиком ПО. Полагаю, что не нужно объяснять какую боль может причинить переход на новую версию библиотеки или фреймворка. Апгрейды - это неотъемлемая часть жизни и вместе с Angular, они больше не будут болью.
Я долго работал с Angular и видел все апдейты с первого релиза (я даже работал с AngularJs, но предпочитаю не говорить об этом). Безусловно Angular прошёл долгий путь, как и CLI. Где в 2018 году в Angular CLI появилась ещё одна команда - ng update. Вы можете использовать её так:
ng update @angular/core
Дальше происходит магия. Все зависимости ядра Angular обновятся до последней версии. Если ваш код нуждается в обновлении, CLI сделает это за вас и, если нельзя, то скажет, где вам нужно самим вручную обновить свой код. Обновление до последней версии Angular займёт от нескольких секунд до нескольких минут, в то время, как с React это может занять от нескольких часов до нескольких дней (или даже недель).
Это отчасти потому, что Angular CLI такой мощный, а отчасти, что команда разработчиков Angular так выкладывает обновления. Они заботятся не о том, чтобы в релиз влезло как можно больше критических изменений, которые заставят разработчиков полностью переписать свои приложения. Они всегда следят за тем, чтобы критические изменения были обратно совместимыми для одной или двух предыдущих версий, чтобы дать возможность разработчикам внести большие изменения, если необходимо. Я пользуюсь этим постоянно и это мощное средство для обслуживания Angular
Давайте свяжем всё вместе
Хорошо, в этой статье мы о многом поговорили, и мне кажется, что я бы хотел ещё многое сказать. Тем не менее, мне кажется я коснулся всех болевых точек, которые я упоминал в начале статьи.
Используя возможности Angular и Angular CLI, вы сможете сделать ваши приложения более совместимыми. В то время как некоторые приложения могут отличаться настройками, принципы остаются одинаковыми, по-этому порог вхождения в новый проект будет не таким уж высоким. Один раз выучи - применяй везде!
Хорошая совместимость - уменьшение сложности обслуживания. Когда ваш проект растёт, стоимость обслуживания не будет возрастать экспоненциально. Наверное это самая большая проблема в React приложениях, которую превосходно решает Angular. Следуя тому же ходу мысли, мы также получаем всё, что нам нужно прямо из ядра Angular. Обработка форм? Пожалуйста. Роутинг? Пожалуйста. Ленивая загрузка? Пожалуйста. Я мог бы продолжить, но остановлюсь на этом. Даже если вы что-то не используете, то это не войдет в ваш билд, потому что всё, что в ядре Angualr является оптимизируемым деревом (tree shakable), включая рендеринг.
Чтобы завершить статью, скажу последнее. Используйте Angular. Смотрите видео на YouTube. Читайте документацию или учитесь, как у вас получается лучше всего. А затем смело бросайте React в мусор, потому что он вам больше не понадобится.
Если вы крупная компания с множеством фронтэндных приложений и вы заставляете разработчиков использовать React, то пересмотрите свой подход. Если вы хотите иметь группу разработчиков, которая может легко переходить от проекта к проекту, разрабатывать новые приложения, добавлять фичи с головокружительной скоростью и иметь дешёвых в обслуживании фронтэнд проектов, то садитесь на поезд под названием Angular. Если у вас такой бизнес, то React будет вас ограничивать.
Alexandroppolus
На самом деле это правильно. Компоненты-классы ну просто не могут не нарушать SRP, потому что как минимум они рендерят картинку. И "делать кроме этого что-то ещё" — так себе идея. Потому выносим всю логику в отдельные классы, а представлению (реакт-компоненту) оставляем его единственную обязанность.
essome
Только нормальные разрабы и в классах норм напишут, при этом не потеряют удобства классов, а говнокодеры и в функциях логики наплодят
AxisPod
Так чем функциональные компоненты спасают от SRP? Голые может быть и спасли бы, но в этом случае вы не способны реализовать полноценный компонент, либо это затребует слишком большого оверхеда в виде большущей куче мелких компонентов, которые вы более нигде и никогда использовать всё равно не будете.
В React же дали хуки, что привели на деле только к куда более худшей ситуации. Код стал менее тестируемым, с использованием IoC от Angular вы тестируете сервис, без каких-либо зависимостей и вы действительно знаете, его зависимости и спокойно заменяете мок-зависимостями (фейками, пустышками и т.д.), в React вы не знаете зависимости, в принципе, мокнуть по правильному код реакта просто невозможно, вам так или иначе придётся лезть в приватный код, чтобы хоть как-то протестировать компонент.
А ведь какой-нибудь простенький компонент может к примеру запрашивать по http данные, это в React будет реализовано в useEffect на старте или в useCallback по клику мышкой. Как вы будете подменять данный код в тестах? У вас просто нет легальной возможности это сделать.
P.S. Мы сейчас работаем над визуальной IDE на React, с кучей готовых компонентов, изначально я не хотел выбирать React + Redux для этой задачи, но сверху сказали. Теперь проект превратился в какой-то адский ад. Тестировать невозможно. Вынести адекватно бизнес-логику совершенно отдельно от React и Redux просто невозможно, а её ну очень много. React не поддерживает IoC и мы в общем не можем вынести общие сервисы для работы в рамках компонентов и редьюсеров, всю логику нельзя унести в редьюсеры, это потребует очень большого оверхеда. Ах да, структура приложения древовидна, редакс в принципе не предназначен для этого, любой чих в итоге — полная перерисовка всего. Потребовалось делать подсистему кэширования. В итоге проект превратился в сборище костылей лишь потому, что выбран неподходящий ниструмент.
Alexandroppolus
Всё указанные трудности — из-за этого. Вы не умеете готовить Redux )) Я тоже не умею. Попробуйте связку React + TypeScript + MobX, там можно напроектировать хорошую ООП-структуру приложения — именно логики и связанного с логикой состояния, начисто убирая их из компонентов, делая правильные зависимости через DI, по всем канонам. Это будет совсем другой Реакт, в котором не найдется места компонентам-классам.
MaZaAa
О ужас, DI во фронтенде редкостная дичь. Ещё и расписали это как «правильное» и «по всем канонам», хахах,
Возможно для тех, кто любим тебе усложнять жизнь на ровном месте да, им подойдет эта дичь и не только, надо идти глубже и брать ещё и Redux и т.п. для полного «кайфа».
AxisPod
Ну давай, расскажи, как же готовить Redux для работы с древовидными данными? Когда конечное приложение - дерево. И уж MobX не надо тут рекламировать, я понимаю, он вам нравится, но минусов у него достаточно. Вообще в нашей архитектуре куда лучше подойдёт RxJS, хотя смотрим сейчас на Effector и потихоньку рефакторим приложение, для безболезненного перехода, точнее менее безболезненного. Ибо бизнес-логику надо будет перелопачивать, знатно перелопачивать.
А вот DI должен быть либо везде, либо нигде, ведь идея в том, что создаётся только базовый объект приложения, далее всё разруливает именно IoC контейнер. В ином случае просто теряется весь смысл. О функции "resolve" надо забыть, вызываться должна ровно 1 раз. В случае с React этого добиться невозможно. Собственно выйдет знатный антипаттерн.
Ещё минус React, не скажу как внутри, но снаружи он синхронен, в этом его колосальная проблема. Так или иначе появляется слой перехода асинхронного и синхронного. Кругом нужны костыли для поддержки полноценной асинхроннсти. После к примеру WPF и ASP.NET MVC на это очень больно смотреть.
MaZaAa
Как минимум не использовать Redux. Вообще в приложениях любого типа его нельзя использовать, но с другой стороны если хочется ныть как все плохо, то используйте конечно.
Я конечно понимаю, если взять MobX, то больше не будет проблем и жизнь станет легка и непринужденна, но ведь тогда не получится ныть и искать адовые пути решения элементарных задач.
Вот к примеру ваша, абсолютно элементарная «проблема» — древовидная структура данных, и что? Где проблема-то? Mobx просто создан для этого. Посмотрите на досуге что такое javascript Proxy, уже древняя штука по меркам 2021 года.
state.cars[0].passangers[4].name = 'new name'; и все, перендерится только один конпонент, вот прям только тот, который читает «name» у объекта этого пассажира.
Но нет же, это же так легко и просто, вы лучше пройдете адовый путь впаре с RxJS и/или Effector'ом и заодно разорите работодателя, заведомо обреча код проекта на провал и полное переписывание с нуля.
Какие минусы? Перечислите пожалуйста, и желательно реальные минусы, а не бред сивой кобылы или джунов которые что-то где-то прочитали/увидели и решили что раз так написали, значит это не просто так и действительно так оно и есть.
Например периодически встречаю псевдо «минус» — «он мутабильный», смешно и абсурдно конечно, но реально кто-то так думает.
Сочувствую, боль, разочарование и кандидат номер один на полное переписывание с нуля из-за невозможности дальнейшего развития/поддержки будущего «чуда», а так же невозможность нанять адекватных разработчиков т.к. разумеется с такой белебердой никто иметь дел не будет.
P.S. выше вы писали:
Что я могу сказать, прогибаться и потом страдать, при том что сразу же знаешь наперед что будешь страдать, так себе жизненная позиция… Вы сами себя так поставили и на этом месте работы на вас будут ездить.
У меня например все просто, как только приходит какой-то неадекватный ультиматум или проект на котором я «должен теперь работать» «сверху», я просто перехожу на другую работу. Зачем страдать если можно не страдать? Благо предложений на рынке завались. Другое дело если бы у вас была такая профессия где предложений нет вообще, то от безисходности придется потерпеть т.к. тут уже вопрос умереть с голоду или нет)
TerraV
Несмотря на такую агрессивненькую подачу материала, я в целом согласен. ReactiveX имеет высокий порог входа и для большинства команд это мина замедленного действия. Сегодня работает а завтра ушла пара людей с техкомпетенциями и приплыли. MobX не прост, но он, сцуко, работает ) И да, если молча соглашаться на то что предлагают сверху, это прямой путь в психоневродиспансер. Потому что в случае успеха — это успех кого надо успех, а косяки это только твои косяки.
AxisPod
Большая проблема MobX, это оборачивание в Proxy для реализации observable, собственно это ещё грозит потерями ссылок, в итоге объекты уже не сравнить по ссылкам после MobX. При этом оборачивание в глубину. Я конечно понимаю, что можно вырубить, но тогда теряется весь смысл MobX. Производительность от этого местами страдает ничем не меньше чем с Redux. Если уже и смотреть, то лучше в сторону MST (MobX State Tree), он не оборачивает в Proxy, но требует строгого описание типов, что на деле другая, совсем не маленькая проблема.
Почему в нашем случае MobX не подойдёт, у нас хранятся таблицы, временами на сотни тысяч строк, если это ещё заворачивать в Proxy, аааа, будет кошмар, оно и без этого сильно тормозит при обработке таблиц. В нашем случае иммутабельность даже является спасением.
MaZaAa
Вот как бы и всё, о какой ещё потери ссылок вы говорите? Вы должны работать с someStore.items.
Ну уж нет, MST это убожество, тем более если речь идет о производительности, то у MST она самая худшая.
1) Хоть на миллион, с чего вдруг они все должны быть разом добавлены в DOM дерево, если пользователь их не видит глазами, пока не начнет скролить?
2) Да вы можете вообще без стейт менеджеров обойтись и будет у вас максимум производительности. Вот наводка и рабочий пример, вообще без стейт менеджеров и на любой объем данных: codesandbox.io/s/adoring-nightingale-5m227?file=/src/App.tsx
Суть приема думаю ясна.
Иммутабельность всегда медленее мутабильности и прожорлевее по памяти, сейчас речь не о MobX, а вообще в целом.
Alexandroppolus
Для справки: кроме обычного observable (которое по сути своей observable.deep), есть ещё observable.ref и observable.shallow. Первый отслеживает только значение наблюдаемого поля, но не залазит внутрь. Второй залазит на один уровень в глубину. С помощью observable.ref и computed можно запилить абсолютно ту же самую схему с иммутабельностью, которая делается в редуксе со стейтом и мемоизированными селекторами.
Вышеупомянутый observable.deep, надо заметить, тоже не всегда "deep". Он не залезает внутрь произвольных объектов (которые не Object и не Array).
Таким образом, глубиной наблюдения можно довольно тонко управлять. И основная идея — стараться делать замены по возможности на "границе наблюдения". То есть, например, у вас развесистое дерево observable.deep, тогда если просто взять и поменять от корня (в иммутабельном стиле) — долго, МобХ должен обойти всё, посносить везде подписки, если есть. А потом обработать новый объект. А вот листики меняются быстро.
С MST не работал, но вроде это просто нашлепка поверх обычного мобикса, то есть в основе тот же механизм. Может, ошибаюсь.
Alexandroppolus
Ну написал же, что не умею. Чего глумиться-то?..
Какие, например, у него минусы?
Выглядит, как будто вы заблудились в названиях и не знаете что именно хотите.
Ну вообще, DI — идея, IoC контейнер — реализация. Реализация для идеи, но не наоборот.
Да, в реактовские компоненты надо вручную, хуками, затаскивать функциональность, ну и что? Это не так страшно выглядит.
Что значит "синхронен"? Это слой представления, он не должен мутить всякую асинхронщину. По сути — просто визуализация текущего состояния, плюс обработка действий пользователя. Не надо пилить логику на Реакте, тогда не будет трудностей.
nin-jin
IoC контейнер — это костыль ко кривой идее DI (которая является реализацией идеи IoC), чтобы превратить её в более адекватный подход к IoC — CL.
https://en.wikipedia.org/wiki/Inversion_of_control#Implementation_techniques
Приколы начинаются, когда делаешь виртуализированный рендеринг с ленивой загрузкой данных. И тут уже визуализация выступает инициатором действий и должно дожидаться их завершения. И тут уже подход "я только показываю, моя хата с краю" не работает.
AxisPod
Даже сделать страницу, которая показывает некий документ с сервера. Так или иначе в в стартовом useEffect надо инициировать запрос, потом надо собственно дождаться ответа, проверить не размаунтился ли уже компонент, обновить состояние. Это уже асинхронщина.
Идём дальше, у нас master-slave представление, где детали подтягиваются с сервера непосредственно при выборе элемента в master представлении.
Идём дальше, у нас есть чарт, который должен регулярно обновлять данные, система мониторинга.
И всё это должен инициировать некий компонент, обрабатывать, оно само по себе жить не будет.
А идём ещё дальше, необходимо обрабатывать много данных, заводим WebWorker, в нём обработка, UI не тормозит, но ожидать результат требуется, это опять асинхронщина.
Alexandroppolus
Повторюсь: не надо пилить логику на Реакте. Все эти запросы, промисы, ожидания и прочее — на уровне логики. Представление само по себе не должно делать запросы и (особенно) чего-то дожидаться, а потом ещё проверять на размаунт. Представление читает стейт, а так же следит за стейтом и за действиями пользователя. А стейт может содержать полученные данные, либо состояние загрузки/обработки или ошибки. Но сами по себе эти сведения "синхронные", в готовом виде.
Возьмем, к примеру, модный нынче useQuery. Думаете, он возвращает промис, раз он "query"? Нифига. Он возвращает реактивную структурку с разной хренью, а запросы и кэширования делает на уровне своей модели, живущей где-то в контексте.