А как в реакте не генерить портянки, если другого способа не предоставлено?
> Декомпозиция, все дела, я уже устал это повторять.
То, что вы разделите ручную генерацию портянок на несколько кусков — ничем не поможет. Более того — оно вредит. Потому что мы возвращаемся к началу — из-за искусственного чрезмерного разбиения процесс генерации становится запутанным и в нем сложнее разобраться. Вместо того чтобы посмотреть на шаблон и сразу понять примерно результат, вам надо собрать по кускам все ф-и и проследить логику их работы.
> Такой же факт подтверждают те, кто пишет вот такой бред:
А в чем проблема? код хорошо структурирован, ветки зрительно отделены, логика ловится с простого взгляда. Более того — все это прекрасно скалируется на шаблоны любого размера. Сравните с ?:, где если ветка длиннее двух строк — вы вынуждены ее отделять, потому что иначе код даже выровнять-то по-человечески затруднительно.
> и мы уже наконец закроем тему синтаксиса.
Я не затрагивал тему синтаксиса.
> Давайте вспомним
Ок. Утверждение «HOC является компонентом» — ложно, так как существуют HOC, которые компонентами не являются. В точности так же, как является ложным утверждение вида «Человек — это негр». На этом спор по части терминологии предлагаю закрыть.
> А как же Host в связке с @HostBinding и @HostListener?
Это реализация директивы, а не ее использование. Вы когда директиву используете, то вообще не в курсе, какие там @HostListener внутри. У вас задача — сделать, чтобы компонент мигал. Это свойство вида, которое никак не затрагивает логику работы самого компонента. По-этому информация о том, что компонент — мигучий, расположена в разметке, это вид.
> а в том, что вместо явного создания нового компонента через HOC с подмешанным поведением, приходится прямо из маркапа обвешивать несчастного дополнительными конструкциями
Если бы в jsx можно было использовать компоненты как <Hoc(Component) .../>, не объявляя промежуточных, то большинство бы так и делало. И было бы это таким же обвешиванием из маркапа. И от того, что вы промежуточную переменную создаете — в сущности ничего не меняется.
> Еще раз, в ангуляре нет HOC. Component — это не компонент. Компонент — это результат его выполнения.
Конечно.
> Так вот результирующий компонент уже никак (почти) не может быть компонентом высшего порядка.
Он и не должен. HOC — это Component, а не результат работы Component.
> А вот вернуть другой компонент при «вызове» этот уже никак не может.
Но Component возвращает компонент при вызове. Любой декоратор это делает.
> Никак я не могу обернуть компонент так, чтобы получился новый компонент, рендерящий старый с дополнительными свойствами, без dom-обертки.
В классе компонента ангуляра _нет функции рендер_. По-этому когда вы делаете в ангуляре HOC, то вы никак не меняете разметку и поменять ее не можете — потому что нельзя поменять то, чего нет. HOC могут поменять «логику работы», например — взять обычный компонент и сделать из него мигающий компонент. Прямо как директива. Понимаете? Именно по-этому я сравниваю именно с директивой — потому что в директиве вы не можете переопределить разметку, внутри директивы у вас ровно те же ограничения, что и в HOC. По-этому:
> Так что вопроса как такового нет. Все используют директивы, потому-что по-другому никак.
Очень даже «как». Практически все, что может директива, можно сделать при помощи НОС. Но НОС не используют.
Еще раз — НОС не используют не для «переопределения рендер функции» (это и директивой нельзя), а для тех вещей, который можно сделать директивой. И их же (в основном) можно сделать в НОС.
> Все, прекратите, каша из звездочек, скобочек и «коробочек» уж никак не лучше обычных языковых конструкций, субъективщина чистой воды.
Это объективный факт. Сообщество много лет назад давным-давно признало, что генерить портянки руками — ад и погибель, и все перешли на шаблонизаторы в том или ином виде. То, что на волне хайпа фейсбуку удалось продать наивным, не нюхавшим пороху пионерам от фронтенда фреймворк, предназначенный для портирования протухшего легаси-кода с php на js — просто исторический казус.
Тот факт, что jsx нечитаем — подтверждают сами люди, которые пишут на jsx, когда выделяют ветки тех же ?: в отдельные ф-и.
> Высшего порядка? Высшего.
Нет, конечно. Это обычный компонент, который возвращает обычный кусок ДОМ. HOC же возвращает компонент, а не кусок ДОМ.
> Только меня смущает не это, а то, что добавление поведения к модели (классу компонента) происходит из маркапа другого компонента.
Никто никакого поведения к модели не добавляет. То, что ваш контрол мигает — это не свойство модели, это свойство вида и исключительно его. В модели оно вообще никак не отражается, точно так же как в модели никак не отражается размер шрифта или цвет фона (если только изменение цвета фона не связано с логикой). То, что в реакте любое изменение чего-либо может быть реализовано _только_ через изменение модели (если, конечно, не теребить дом-ноды напрямую, что уже какбы и не реакт, а обход реакта) — это просто свойство конкретно реакта. В других фреймворках может быть другая ментальная модель, не пытайтесь натягивать одно на другое.
И, в итоге, так и не получается ответить на вопрос: если в ангуляре есть и директивы, и HOC, которые могут выполнять одну и ту же задачу, и HOC решают эту задачу лучше, то почему все используют директивы?
Нет, не одинаковые. В реакте у вас map'ы и ?:, которые делают код совершенно нечитабельным. В итоге приходится выделять содержащий подобные вещи код в отдельные функции. Которые иначе были бы не нужны.
> и степень вырвиглазности в отвратного DSL ангуляра повыше будет
Даже плохой DSL почти всегда лучше универсального языка. Именно по-этому DSL создаются (ведь это требует дополнительной работы), именно по-этому от jsx-подходов на бекенде отказались давным-давно.
> HOC — higher order Component.
HOC — не компонент. Как бы он ни назывался. Он не обладает свойствами компонента, его нельзя использовать как компонент. Обратите внимание на аналог — HOF. HOF — по факту _является_ ф-ей, обладает св-вами функции, может использоваться как ф-я. Видите разницу? В любом случае, давайте не будем заниматься спором о терминологии, мы говорим о конкретной сущности, а как ее называть — дело десятое.
> Ну по вашей логике мне нужно каждый мой «компонент» обернуть в эту функцию, чтобы движок понял, что это «компонент».
Нет, это не так, конечно. Любой класс является классом компонента сходу, а Component — лишь связывает этот класс с его видом, добавляя во внешнюю конфигурацию соответствующую информацию. Сообщает движку, что «если ты увидишь этот селектор то надо создать данный вид и привязать к указанному компоненту». У одного компонента может быть много видов, к слову. Вы же сами в курсе, что аннотации из Component не наследуются — потому что они не являются св-вами полученного класса, и вообще к этому классу отношения не имеют. Иными словами, класс компонента в ангуляре не содержит ф-и «render». Эта ф-я живет отдельно от компонента и к нему не привязана. С точки зрения ментальной модели реакта это трудно понять, т.к. в реакте как раз ф-я «render» сама по себе уже является компонентом (вид = модель). Но фреймворки разные, ничего удивительного. Надо эту разницу в рассуждениях учитывать.
> Нет нельзя, так как у вас появится dom-обертка.
При использовании HOC не появляется дом-обертки. Откуда ей взяться? Напоминаю, что директивы в ангуляре не используются для генерации дом, они используются для подмешивания поведения. То есть у вас уже есть компонент и ваша директива будучи к нему примененной бэкграунд меняет по таймеру. Все то же самое можно сделать с HOC и никаких оберток не будет, им неоткуда взяться. Но используют в таких случаях директивы, а не НОС.
> Поведение модели должно определяться и расширяться моделью, а не нагромождением заклинаний в маркапе.
В маркапе никакой модели нет. Модель — класс компонента. Он существует независимо от маркапа, маркап — существует независимо от класса компонента. Один маркап можно использовать с разными моделями. Одну модель можно использовать с разными маркапами. Связывание маркапов с моделями производится на внешенм этапе конфигурирования. Внешнем в том смысле, что ни сам маркап, ни модель, это конфигурирование не затрагивает. Вас смущает тот факт, что дефолтный вариант работы — это аннотация на классе. Но это просто дефолт, удобное стандартное поведение, которое подходит в большинстве кейзов и потому специально реализовано.
> Это-то и понятно. Если перечитаете мой предыдущий комментарий, поймете, к чему такие вопросы.
Перечитал. Не понял. Реакт у вас требует выделять компоненты, чтобы код не был чересчур вырвиглазен. Ангуляр не требует. В итоге, в ангуляре (и других фреймворках с полноценными шаблонизаторами) вы можете выделять компоненты там, где это разумно с точки зрения общей целесообразности, а в реакте — не можете (ну или можете, но качество кода будет значительно хуже).
> HOC — это компонент, принимающий в качестве «аргумента» другой/другие компоненты и инстанцирующий/рендерящий их, а не просто декоратор.
Во-первых, HOC — не компонент. Это функция. Функция, которая принимает компонент (функцию или класс) и возвращает компонент (функцию или класс). В каком месте это не «просто декоратор»?
> Но уж вот это никак HOC не является, это обыкновенная аннотация, помогающая дубовому шаблонизатору сообразить какую модель инстанцировать при встрече нужно селектора.
Это не аннотация, это функция, которая принимает класс и возвращает класс. Как-нибудь так:
> Пусть даже и в глобале все так же, но с уникальным селектором.
С каким селектором?
> А где эта середина?
Я же описал. Отдельные куски имеет смысл выделять тогда, когда это обосновано смыслом предметной области. Например, кнопка — это вполне законченный, осмысленный, компонент. Его потом можно брать и вставлять изолированно куда угодно, и он будет выполнять свою вполне определенную и понятную функцию. Аналогично, допустим, какой-то завершенный логически кусок формы. А вот случайный кусок дома, который вне контекста одного единственного компонента никакого смысла не имеет — выделять не надо. Точно так же, к слову, следует подходить к выделению отдельных функций. Если вы за пару секунд не можете придумать для вашей функции нейминг, из которого будет понятно, что конкретно делает функция — значит и не надо ее выделять. Потому что ничего осмысленного она не делает.
> Что, простите?
То, простите. HOC — неотъемлемая часть ангуляра, например, на каждый класс компонента применяется HOC «Component».
> Все используют директивы, так как это единственный вариант подмешать что-то компоненту так, чтобы не было обертки в доме.
HOC тоже не генерит оберток (откуда?). Но используют директивы.
> Я вас спрашивал, чем плохи HOC, не надо опять соскакивать в обсуждение angular vs react.
Я вам привел пример ситуации, в которой есть как HOC, так и другие инструменты. В результате люди используют другие инструменты. Мне вывод кажется очевидным. Мне кажется, что логически невозможно заявлять о преимуществе HOC перед директивами, не объяснив, почему в ангуляре люди выбирают использовать директивы.
При том, что мы ее, вроде, тут обсуждаем? Вот я повесил на дом-ноду какого-то компонента класс, описал для него стиль, как обеспечивается изоляция этого стиля?
> Под «давно уже не инлайнят» я имел в виду CSSOM и экстракт в css-файлы. Или вы о чем?
При чем тут экстракт? Я говорил об использовании. Под «инлайнингом» подразумевался подход, эквивалентный styled components, когда вы тем или иным способом указываете стиль для конкретного дом-элемента, в противовес классическому css подходу с указанием стиля для селектора, то, что сейчас подразумевается под css-in-js. Если вы под css-in-js подразумеваете просто генерацию стилей на ходу — то так оно и в ангуляре css-in-js тогда, там после компиляции никаких .css не остается.
> Ну я даже не знаю… Вы за трех-экранные полотна маркапа? Если нет, то где тогда грань? И как же декомпозиция, как же переиспользуемость?
Грань определяется золотой серединой, как и всегда. Трехэкранные полотна — плохо, двухстрочные компоненты — тоже плохо (и неизвестно, что хуже). Разумная декомпозиция (то есть, семантическая, когда необходимый кусок выделяется по внешним по отношению к коду соображениям) — хорошо и ведет к переиспользуемости. Излишнее дробление, к слову, к ней не ведет, т.к. бессмысленный кусок кода вы больше одного раза использовать не станете.
> а директивы — имхо лютая хрень, размазывающая логику и выглядящая как манки-патчинг. Вопрос религии в общем.
Ну смотрите, в ангуляре есть и HOC и директивы. Все используют директивы и не используют HOC. Вывод очевиден — если людям дать нормальный инструмент, то HOC выбрасывается на помойку.
И как тогда решается проблема с инкапсуляцией классов?
> что плохого?
То, что понять, какой будет верстка, не решив пазл по сборке пары-тройки десятков двухстрочных микро-компонент — невозможно. Если дробления нет — для той же задачи достаточно одного мимолетного взгляда.
> Раскажите, пожалуйста, какие адекватные средства вы имеете в виду
Трансклюд, директивы
> И почему, собственно, HOC плохи?
HOC — это просто декораторы классов, они же миксины. Нет ничего плохого, когда вы используете декораторы по назначению — для подмешивания поведения к оригинальному классу. Но из-за отсутствия альтернатив реакт вынуждает вас писать декораторы на каждый чих, по любому поводу, например, чтобы пробросить кусок дома внутрь компоненты.
В каком случае этого можно не понять? Попробуйте описать его конкретно, пожалуйста. Вот передо мной страница, я тыкнул в элемент, рассматриваю его в инспекторе… Что должно произойти, чтобы я не знал к какому компоненту относится тот или иной элемент?
> компонентами высшего порядка в реакте не пользовались?
Это же известный антипаттерн. В любом случае, мы ведь не про реакт, там все, действительно, совсем плохо — стили инлайнят (привет технологиям десятилетней давности), компоненты сильно дробят, за неимением адекватных средств, действительно, приходится использовать HOC с другими подобными костылями и т.д…
> Трансклюдами в ангуляре
Там же стоит атрибут с квалификатором хост-элемента. Разве что, наверное, было бы лучше делать его _ng- content-имя-суффикс, а не _ng_content-суффикс, но на практике не существует ситуации при которой на то, чтобы выяснить, откуда элемент затрансклюжен, уходит больше пары секунд. В любом случае — это уже именно проблема поиска того, к какому компоненту относится данный элемент (вам же все равно придется это узнать, чтобы что-то исправлять), а не проблема инкапсуляции стилей.
> А есть фреймворки и с более динамичной компоновкой…
Структура документа заранее известна, любой компонент врапнут в соответствующий тег., имена хост-элемента проставлены. Все еще не могу представить такой ситуации, даже с динамическими компонентами. Даже если попробовать добиться такой ситуации специально.
> Но вот незадача, заказчик захотел свой кастомный бутстрап в виде css. И, вот же незадача, у вашего элемента вдруг от куда не возьмись появились паддинги по бокам…
Это проблема реализации (shadow dom эмулирутеся), а не подхода. Перейдут все на нативный shadow dom — проблема сама собой исчезнет.
> Держать это все в голове, думая над именами селекторов?
Зачем? Можно решить эту проблему так, как вы решали бы ее с css-in-js — вместо того, чтобы импортировать свой бутстрап глобально, импортируйте его в каждую компоненту, локально. Все точно так же и с точно тем же результатом.
> Чтобы понять кто что отрендерил и нужны глобально уникальные имена.
Зачем? Если вы знаете что нода от компонента Х — то у нее стили именно от этого компонента.
> Или вы предлагаете интерпретировать код у себя в голове, пытаясь понять какой из элементов к которому из нескольких одноимённых компонент относится?
Не могу себе даже в теории представить ситуацию, когда непонятно к какому компоненту относится инспектящаяся нода. Без каких-либо интерпретаций в голове. Можете привести пример?
> вам нужно разобраться почему календарик распирает диалог, а каждая из этих 3 труп людей посчитала своим долгом дать своим блокам лаконичные имена вида «main», «box» и тому подобные. В итоге вас в стилях: .main[erucqi], .main[ifdofi] и .main[reoo].
Все дом-ноды с кастомными стилями маркируются соответствующим атрибутом. Такой ситуации, когда непонятно, какой класс откуда прилетел — возникнуть в принципе не может (разве что вы не можете понять откуда вообще отрендерился рассматриваемый вами кусок dom, но тогда вам уже ничего не поможет).
> Вы придираетесь к словам, понятно же, что типизация структурная.
Это не придирка к словам, это то как работает алгоритм тайпчека (и от этих «придирок» он будет работать с разным результатом)
> Окей, для ветки if (x instanceof A), x содержит все поля интерфейса A. Так лучше?
Да, именно это утверждение формулируется на уровне типов. Но, вот проблема, сам instanceof в рантайме поля не проверяет. По-этому мы можем быть уверены в том, что в позитивной ветке поля есть. Но не можем быть уверены в том, что их нету в негативной.
Не интерфейсом, а _его полями_. В тайпскрипте нету номинальных типов. Нигде нету. И instanceof типизируется структурно (как и все остальное), по наличию полей, а не номинально. Утверждения вида «Х реализует интерфейс А» невозможно выразить на тайпскрипте. На уровне типизации нету никаких классов, интерфейсов и прототипов вообще. Есть только типы, которые либо содержат какие-либо поля, либо не содержат. Единственные утверждения, которые вы можете делать на тайпскрипте: «Х содержит поле Y».
> Из этого следует, что в цепочке прототипов x есть A.prototype, то есть у x есть поля, перечисленные именно в A.
В Тайпскрипте нет такого отношения. A < B => А содержит все поля B. Проверка типов для instanceof не может проверить что-либо кроме этого, потому что на уровне типов тайпскрипта ничто иное невыразимо. По-этому soundness чекер не должен выводить ничего для негативной ветки. Но в тайпскрипте намеренно жертвуют soundness ради удобства и более простой типизации существующего кода и привычных для жс паттернов, так что вполне возможно, что и тут оно — by design.
Потому что такая семантика у instanceof. Из x instanceof A следует, что в х есть поля А, но из того факта, что !(x instanceof A) не следует, что в х нет полей А, иными словами, у нас нету никаких корректных утверждений о типе значения в негативной ветке. Мы не можем утверждать, что в негативной ветке у нас тип !A, а значит, и не можем утверждать, что он A | B — A = B. По-этому narrowing должен быть только в позитивных ветках.
> К сожалению, смесь двух видов типизации до добра не доводит:
Никакой смеси нет. В тайпскрипте только структурный сабтайпинг (на данный момент). В вашем примере по факту, баг occurence typing'а — чекер не должен уметь выводить тип B для х в негативной ветке, только А в позитивной. Впрочем, это могло быть сделано и специально (правильное поведение вынудило бы переписывать ооочень много подобных кейзов).
Активный вид или не активный — это совершенно неважно и никак на наличие прослойки не влияет. В описании MVC нету указаний на то, какой там вид. Если вы прочитаете любую из ранних статей по MVC, то вы увидите ровно две вещи:
1. везде контроллер определяется как прослойка между входами и остальной частью системы
2. нигде контроллер не выступает в качестве прослойки между видом и моделью
Если хотя бы одно из условий не выполнено — то у вас не MVC. Совершенно определенно, может быть разница в деталях (активный/пассивный вид, например, к слову, по вашей ссылке схема для пассивного вида неправильная — она от варианта с активным видом должна отличаться только лишь тем, что стрелочка «модель — вид» направлена в другую сторону), но идея паттерна (в случае MVC — отсутствие прослоек между видом и моделью и наличие элемента, который отвечает за обработку входов) — должна сохраняться.
> Контроллер это USB коннектор между моделью и видом.
Это не так, я выше уже цитировал:
> Controllers contain the interface between their associated models and views and the input devices
(keyboard, pointing device, time).
Предназначение контроллера — именно изоляции системы от действий пользователя. Это основная его функция в MVC.
> В принципе, он может вообще не обрабатывать никакие события.
Тогда он не нужен, так как не выполняет свою основную функцию.
> Его задача состоит в отделении моделей от видов.
Модель от вида в MVC прослойкой не отделяется (более того — вид вообще по MVC вполне может делать запросы о состоянии модели напрямую). Потому что это приводит к огромным проблемам (см. выше пример), и при этом не приносит никакой пользы. Чтобы разместить прослойку между видом и моделью, необходимо сначала разбить связь между контроллером и моделью. Тогда контроллер естественным образом включается в вид, прослойка называется «Presenter», и вы получаете MVP вместо MVC.
А как в реакте не генерить портянки, если другого способа не предоставлено?
> Декомпозиция, все дела, я уже устал это повторять.
То, что вы разделите ручную генерацию портянок на несколько кусков — ничем не поможет. Более того — оно вредит. Потому что мы возвращаемся к началу — из-за искусственного чрезмерного разбиения процесс генерации становится запутанным и в нем сложнее разобраться. Вместо того чтобы посмотреть на шаблон и сразу понять примерно результат, вам надо собрать по кускам все ф-и и проследить логику их работы.
> Такой же факт подтверждают те, кто пишет вот такой бред:
А в чем проблема? код хорошо структурирован, ветки зрительно отделены, логика ловится с простого взгляда. Более того — все это прекрасно скалируется на шаблоны любого размера. Сравните с ?:, где если ветка длиннее двух строк — вы вынуждены ее отделять, потому что иначе код даже выровнять-то по-человечески затруднительно.
> и мы уже наконец закроем тему синтаксиса.
Я не затрагивал тему синтаксиса.
> Давайте вспомним
Ок. Утверждение «HOC является компонентом» — ложно, так как существуют HOC, которые компонентами не являются. В точности так же, как является ложным утверждение вида «Человек — это негр». На этом спор по части терминологии предлагаю закрыть.
> А как же Host в связке с @HostBinding и @HostListener?
Это реализация директивы, а не ее использование. Вы когда директиву используете, то вообще не в курсе, какие там @HostListener внутри. У вас задача — сделать, чтобы компонент мигал. Это свойство вида, которое никак не затрагивает логику работы самого компонента. По-этому информация о том, что компонент — мигучий, расположена в разметке, это вид.
> а в том, что вместо явного создания нового компонента через HOC с подмешанным поведением, приходится прямо из маркапа обвешивать несчастного дополнительными конструкциями
Если бы в jsx можно было использовать компоненты как <Hoc(Component) .../>, не объявляя промежуточных, то большинство бы так и делало. И было бы это таким же обвешиванием из маркапа. И от того, что вы промежуточную переменную создаете — в сущности ничего не меняется.
> Еще раз, в ангуляре нет HOC. Component — это не компонент. Компонент — это результат его выполнения.
Конечно.
> Так вот результирующий компонент уже никак (почти) не может быть компонентом высшего порядка.
Он и не должен. HOC — это Component, а не результат работы Component.
> А вот вернуть другой компонент при «вызове» этот уже никак не может.
Но Component возвращает компонент при вызове. Любой декоратор это делает.
> Никак я не могу обернуть компонент так, чтобы получился новый компонент, рендерящий старый с дополнительными свойствами, без dom-обертки.
В классе компонента ангуляра _нет функции рендер_. По-этому когда вы делаете в ангуляре HOC, то вы никак не меняете разметку и поменять ее не можете — потому что нельзя поменять то, чего нет. HOC могут поменять «логику работы», например — взять обычный компонент и сделать из него мигающий компонент. Прямо как директива. Понимаете? Именно по-этому я сравниваю именно с директивой — потому что в директиве вы не можете переопределить разметку, внутри директивы у вас ровно те же ограничения, что и в HOC. По-этому:
> Так что вопроса как такового нет. Все используют директивы, потому-что по-другому никак.
Очень даже «как». Практически все, что может директива, можно сделать при помощи НОС. Но НОС не используют.
Еще раз — НОС не используют не для «переопределения рендер функции» (это и директивой нельзя), а для тех вещей, который можно сделать директивой. И их же (в основном) можно сделать в НОС.
Это объективный факт. Сообщество много лет назад давным-давно признало, что генерить портянки руками — ад и погибель, и все перешли на шаблонизаторы в том или ином виде. То, что на волне хайпа фейсбуку удалось продать наивным, не нюхавшим пороху пионерам от фронтенда фреймворк, предназначенный для портирования протухшего легаси-кода с php на js — просто исторический казус.
Тот факт, что jsx нечитаем — подтверждают сами люди, которые пишут на jsx, когда выделяют ветки тех же ?: в отдельные ф-и.
> Высшего порядка? Высшего.
Нет, конечно. Это обычный компонент, который возвращает обычный кусок ДОМ. HOC же возвращает компонент, а не кусок ДОМ.
> Только меня смущает не это, а то, что добавление поведения к модели (классу компонента) происходит из маркапа другого компонента.
Никто никакого поведения к модели не добавляет. То, что ваш контрол мигает — это не свойство модели, это свойство вида и исключительно его. В модели оно вообще никак не отражается, точно так же как в модели никак не отражается размер шрифта или цвет фона (если только изменение цвета фона не связано с логикой). То, что в реакте любое изменение чего-либо может быть реализовано _только_ через изменение модели (если, конечно, не теребить дом-ноды напрямую, что уже какбы и не реакт, а обход реакта) — это просто свойство конкретно реакта. В других фреймворках может быть другая ментальная модель, не пытайтесь натягивать одно на другое.
И, в итоге, так и не получается ответить на вопрос: если в ангуляре есть и директивы, и HOC, которые могут выполнять одну и ту же задачу, и HOC решают эту задачу лучше, то почему все используют директивы?
Нет, не одинаковые. В реакте у вас map'ы и ?:, которые делают код совершенно нечитабельным. В итоге приходится выделять содержащий подобные вещи код в отдельные функции. Которые иначе были бы не нужны.
> и степень вырвиглазности в отвратного DSL ангуляра повыше будет
Даже плохой DSL почти всегда лучше универсального языка. Именно по-этому DSL создаются (ведь это требует дополнительной работы), именно по-этому от jsx-подходов на бекенде отказались давным-давно.
> HOC — higher order Component.
HOC — не компонент. Как бы он ни назывался. Он не обладает свойствами компонента, его нельзя использовать как компонент. Обратите внимание на аналог — HOF. HOF — по факту _является_ ф-ей, обладает св-вами функции, может использоваться как ф-я. Видите разницу? В любом случае, давайте не будем заниматься спором о терминологии, мы говорим о конкретной сущности, а как ее называть — дело десятое.
> Ну по вашей логике мне нужно каждый мой «компонент» обернуть в эту функцию, чтобы движок понял, что это «компонент».
Нет, это не так, конечно. Любой класс является классом компонента сходу, а Component — лишь связывает этот класс с его видом, добавляя во внешнюю конфигурацию соответствующую информацию. Сообщает движку, что «если ты увидишь этот селектор то надо создать данный вид и привязать к указанному компоненту». У одного компонента может быть много видов, к слову. Вы же сами в курсе, что аннотации из Component не наследуются — потому что они не являются св-вами полученного класса, и вообще к этому классу отношения не имеют. Иными словами, класс компонента в ангуляре не содержит ф-и «render». Эта ф-я живет отдельно от компонента и к нему не привязана. С точки зрения ментальной модели реакта это трудно понять, т.к. в реакте как раз ф-я «render» сама по себе уже является компонентом (вид = модель). Но фреймворки разные, ничего удивительного. Надо эту разницу в рассуждениях учитывать.
> Нет нельзя, так как у вас появится dom-обертка.
При использовании HOC не появляется дом-обертки. Откуда ей взяться? Напоминаю, что директивы в ангуляре не используются для генерации дом, они используются для подмешивания поведения. То есть у вас уже есть компонент и ваша директива будучи к нему примененной бэкграунд меняет по таймеру. Все то же самое можно сделать с HOC и никаких оберток не будет, им неоткуда взяться. Но используют в таких случаях директивы, а не НОС.
> Поведение модели должно определяться и расширяться моделью, а не нагромождением заклинаний в маркапе.
В маркапе никакой модели нет. Модель — класс компонента. Он существует независимо от маркапа, маркап — существует независимо от класса компонента. Один маркап можно использовать с разными моделями. Одну модель можно использовать с разными маркапами. Связывание маркапов с моделями производится на внешенм этапе конфигурирования. Внешнем в том смысле, что ни сам маркап, ни модель, это конфигурирование не затрагивает. Вас смущает тот факт, что дефолтный вариант работы — это аннотация на классе. Но это просто дефолт, удобное стандартное поведение, которое подходит в большинстве кейзов и потому специально реализовано.
Перечитал. Не понял. Реакт у вас требует выделять компоненты, чтобы код не был чересчур вырвиглазен. Ангуляр не требует. В итоге, в ангуляре (и других фреймворках с полноценными шаблонизаторами) вы можете выделять компоненты там, где это разумно с точки зрения общей целесообразности, а в реакте — не можете (ну или можете, но качество кода будет значительно хуже).
> HOC — это компонент, принимающий в качестве «аргумента» другой/другие компоненты и инстанцирующий/рендерящий их, а не просто декоратор.
Во-первых, HOC — не компонент. Это функция. Функция, которая принимает компонент (функцию или класс) и возвращает компонент (функцию или класс). В каком месте это не «просто декоратор»?
> Но уж вот это никак HOC не является, это обыкновенная аннотация, помогающая дубовому шаблонизатору сообразить какую модель инстанцировать при встрече нужно селектора.
Это не аннотация, это функция, которая принимает класс и возвращает класс. Как-нибудь так:
> См. предыдущий пункт
Практически любую директиву можно переписать в HOC наподобие вашего второго примера. Но так никто не делает. Почему?
С каким селектором?
> А где эта середина?
Я же описал. Отдельные куски имеет смысл выделять тогда, когда это обосновано смыслом предметной области. Например, кнопка — это вполне законченный, осмысленный, компонент. Его потом можно брать и вставлять изолированно куда угодно, и он будет выполнять свою вполне определенную и понятную функцию. Аналогично, допустим, какой-то завершенный логически кусок формы. А вот случайный кусок дома, который вне контекста одного единственного компонента никакого смысла не имеет — выделять не надо. Точно так же, к слову, следует подходить к выделению отдельных функций. Если вы за пару секунд не можете придумать для вашей функции нейминг, из которого будет понятно, что конкретно делает функция — значит и не надо ее выделять. Потому что ничего осмысленного она не делает.
> Что, простите?
То, простите. HOC — неотъемлемая часть ангуляра, например, на каждый класс компонента применяется HOC «Component».
> Все используют директивы, так как это единственный вариант подмешать что-то компоненту так, чтобы не было обертки в доме.
HOC тоже не генерит оберток (откуда?). Но используют директивы.
> Я вас спрашивал, чем плохи HOC, не надо опять соскакивать в обсуждение angular vs react.
Я вам привел пример ситуации, в которой есть как HOC, так и другие инструменты. В результате люди используют другие инструменты. Мне вывод кажется очевидным. Мне кажется, что логически невозможно заявлять о преимуществе HOC перед директивами, не объяснив, почему в ангуляре люди выбирают использовать директивы.
При том, что мы ее, вроде, тут обсуждаем? Вот я повесил на дом-ноду какого-то компонента класс, описал для него стиль, как обеспечивается изоляция этого стиля?
> Под «давно уже не инлайнят» я имел в виду CSSOM и экстракт в css-файлы. Или вы о чем?
При чем тут экстракт? Я говорил об использовании. Под «инлайнингом» подразумевался подход, эквивалентный styled components, когда вы тем или иным способом указываете стиль для конкретного дом-элемента, в противовес классическому css подходу с указанием стиля для селектора, то, что сейчас подразумевается под css-in-js. Если вы под css-in-js подразумеваете просто генерацию стилей на ходу — то так оно и в ангуляре css-in-js тогда, там после компиляции никаких .css не остается.
> Ну я даже не знаю… Вы за трех-экранные полотна маркапа? Если нет, то где тогда грань? И как же декомпозиция, как же переиспользуемость?
Грань определяется золотой серединой, как и всегда. Трехэкранные полотна — плохо, двухстрочные компоненты — тоже плохо (и неизвестно, что хуже). Разумная декомпозиция (то есть, семантическая, когда необходимый кусок выделяется по внешним по отношению к коду соображениям) — хорошо и ведет к переиспользуемости. Излишнее дробление, к слову, к ней не ведет, т.к. бессмысленный кусок кода вы больше одного раза использовать не станете.
> а директивы — имхо лютая хрень, размазывающая логику и выглядящая как манки-патчинг. Вопрос религии в общем.
Ну смотрите, в ангуляре есть и HOC и директивы. Все используют директивы и не используют HOC. Вывод очевиден — если людям дать нормальный инструмент, то HOC выбрасывается на помойку.
И как тогда решается проблема с инкапсуляцией классов?
> что плохого?
То, что понять, какой будет верстка, не решив пазл по сборке пары-тройки десятков двухстрочных микро-компонент — невозможно. Если дробления нет — для той же задачи достаточно одного мимолетного взгляда.
> Раскажите, пожалуйста, какие адекватные средства вы имеете в виду
Трансклюд, директивы
> И почему, собственно, HOC плохи?
HOC — это просто декораторы классов, они же миксины. Нет ничего плохого, когда вы используете декораторы по назначению — для подмешивания поведения к оригинальному классу. Но из-за отсутствия альтернатив реакт вынуждает вас писать декораторы на каждый чих, по любому поводу, например, чтобы пробросить кусок дома внутрь компоненты.
В каком случае этого можно не понять? Попробуйте описать его конкретно, пожалуйста. Вот передо мной страница, я тыкнул в элемент, рассматриваю его в инспекторе… Что должно произойти, чтобы я не знал к какому компоненту относится тот или иной элемент?
> компонентами высшего порядка в реакте не пользовались?
Это же известный антипаттерн. В любом случае, мы ведь не про реакт, там все, действительно, совсем плохо — стили инлайнят (привет технологиям десятилетней давности), компоненты сильно дробят, за неимением адекватных средств, действительно, приходится использовать HOC с другими подобными костылями и т.д…
> Трансклюдами в ангуляре
Там же стоит атрибут с квалификатором хост-элемента. Разве что, наверное, было бы лучше делать его _ng- content-имя-суффикс, а не _ng_content-суффикс, но на практике не существует ситуации при которой на то, чтобы выяснить, откуда элемент затрансклюжен, уходит больше пары секунд. В любом случае — это уже именно проблема поиска того, к какому компоненту относится данный элемент (вам же все равно придется это узнать, чтобы что-то исправлять), а не проблема инкапсуляции стилей.
> А есть фреймворки и с более динамичной компоновкой…
Структура документа заранее известна, любой компонент врапнут в соответствующий тег., имена хост-элемента проставлены. Все еще не могу представить такой ситуации, даже с динамическими компонентами. Даже если попробовать добиться такой ситуации специально.
Это проблема реализации (shadow dom эмулирутеся), а не подхода. Перейдут все на нативный shadow dom — проблема сама собой исчезнет.
> Держать это все в голове, думая над именами селекторов?
Зачем? Можно решить эту проблему так, как вы решали бы ее с css-in-js — вместо того, чтобы импортировать свой бутстрап глобально, импортируйте его в каждую компоненту, локально. Все точно так же и с точно тем же результатом.
Зачем? Если вы знаете что нода от компонента Х — то у нее стили именно от этого компонента.
> Или вы предлагаете интерпретировать код у себя в голове, пытаясь понять какой из элементов к которому из нескольких одноимённых компонент относится?
Не могу себе даже в теории представить ситуацию, когда непонятно к какому компоненту относится инспектящаяся нода. Без каких-либо интерпретаций в голове. Можете привести пример?
Все дом-ноды с кастомными стилями маркируются соответствующим атрибутом. Такой ситуации, когда непонятно, какой класс откуда прилетел — возникнуть в принципе не может (разве что вы не можете понять откуда вообще отрендерился рассматриваемый вами кусок dom, но тогда вам уже ничего не поможет).
Это не придирка к словам, это то как работает алгоритм тайпчека (и от этих «придирок» он будет работать с разным результатом)
> Окей, для ветки if (x instanceof A), x содержит все поля интерфейса A. Так лучше?
Да, именно это утверждение формулируется на уровне типов. Но, вот проблема, сам instanceof в рантайме поля не проверяет. По-этому мы можем быть уверены в том, что в позитивной ветке поля есть. Но не можем быть уверены в том, что их нету в негативной.
В Тайпскрипте нет такого отношения. A < B => А содержит все поля B. Проверка типов для instanceof не может проверить что-либо кроме этого, потому что на уровне типов тайпскрипта ничто иное невыразимо. По-этому soundness чекер не должен выводить ничего для негативной ветки. Но в тайпскрипте намеренно жертвуют soundness ради удобства и более простой типизации существующего кода и привычных для жс паттернов, так что вполне возможно, что и тут оно — by design.
Потому что такая семантика у instanceof. Из x instanceof A следует, что в х есть поля А, но из того факта, что !(x instanceof A) не следует, что в х нет полей А, иными словами, у нас нету никаких корректных утверждений о типе значения в негативной ветке. Мы не можем утверждать, что в негативной ветке у нас тип !A, а значит, и не можем утверждать, что он A | B — A = B. По-этому narrowing должен быть только в позитивных ветках.
Никакой смеси нет. В тайпскрипте только структурный сабтайпинг (на данный момент). В вашем примере по факту, баг occurence typing'а — чекер не должен уметь выводить тип B для х в негативной ветке, только А в позитивной. Впрочем, это могло быть сделано и специально (правильное поведение вынудило бы переписывать ооочень много подобных кейзов).
Активный вид или не активный — это совершенно неважно и никак на наличие прослойки не влияет. В описании MVC нету указаний на то, какой там вид. Если вы прочитаете любую из ранних статей по MVC, то вы увидите ровно две вещи:
1. везде контроллер определяется как прослойка между входами и остальной частью системы
2. нигде контроллер не выступает в качестве прослойки между видом и моделью
Если хотя бы одно из условий не выполнено — то у вас не MVC. Совершенно определенно, может быть разница в деталях (активный/пассивный вид, например, к слову, по вашей ссылке схема для пассивного вида неправильная — она от варианта с активным видом должна отличаться только лишь тем, что стрелочка «модель — вид» направлена в другую сторону), но идея паттерна (в случае MVC — отсутствие прослоек между видом и моделью и наличие элемента, который отвечает за обработку входов) — должна сохраняться.
Это не так, я выше уже цитировал:
> Controllers contain the interface between their associated models and views and the input devices
(keyboard, pointing device, time).
Предназначение контроллера — именно изоляции системы от действий пользователя. Это основная его функция в MVC.
> В принципе, он может вообще не обрабатывать никакие события.
Тогда он не нужен, так как не выполняет свою основную функцию.
> Его задача состоит в отделении моделей от видов.
Модель от вида в MVC прослойкой не отделяется (более того — вид вообще по MVC вполне может делать запросы о состоянии модели напрямую). Потому что это приводит к огромным проблемам (см. выше пример), и при этом не приносит никакой пользы. Чтобы разместить прослойку между видом и моделью, необходимо сначала разбить связь между контроллером и моделью. Тогда контроллер естественным образом включается в вид, прослойка называется «Presenter», и вы получаете MVP вместо MVC.