Pull to refresh

Comments 239

Про отсутствие конфигурации в Create React App — есть обходные пути: react-app-rewired, custom-react-scripts. И для TypeScript существует форк. Кстати, "важные новости": Microsoft выложил TypeScript-React-Starter на базе Create React App.


Prettier vs Standard — в чём разница? Standard ужесточает холиварные вопросы, которые можно конфигурировать в Prettier. Недавно прикрутил Standard и безумно счастлив. Есть несколько плагинов для редакторов кода, которые позволяют использовать Prettier и Standard вместе — оно надо? Пока не разобрался, какая будет польза от совместного использования.


В мире Clojure есть ещё один прекрасный инструмент — Parinfer. Привет табуляции Python и CoffeeScript. Не знаю почему общепринято хейтить CoffeeScript, он меня очаровал этой фишкой настолько, что весь остальной код веб-проектов перевёл на синтаксис с табуляцией: JADE для HTML-шаблонов, Stylus для CSS, YAML для JSON. Увы, всё это в прошлом.

Отвечаю сам себе — беда. :)


Выпилил Standard. Prettier более лоялен. Например, мне нравится оставлять запятые в конце объектов и массивов:


{
  a,
  b,
  c, // запятая позволяет добавлять следующее поле объекта без редактирования предыдущего
}

Ещё довод, CRA рекомендует Prettier.


Но Prettier-ESLint забраковал, он гробит комментарии. Подключил автофикс в хуке на прекоммит, можно пощупать пример.


Что надо для счастья — фрагмент из package.json


{
  "devDependencies": {
    "husky": "^0.13.3",
    "lint-staged": "^3.4.1",
    "prettier": "^1.3.1",
  },
  "scripts": {
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "*.js": [
      "prettier --print-width 100 --single-quote --trailing-comma all --no-semi --write",
      "git add"
    ]
  }
}

Хм интересный способ, мне как-то всегда хватало того, что делает WS перед комитом, или когда я его об этом попрошу, но он далеко не всегда делает то, чего я хочу, например в том же JSX...

Автор пароноик, что плохого в том, что за определенной технологией стоит целая компания? Разве не в ее интересах продвигать свою технолгию, улучшать ее? Это не похожа на ситуацию с IE6, когда Microsoft тормозила веб.

Чаще всего, проекты, за которыми никого нет, они просто вымирают (PhantomJS к примеру), когда заканчивается хайп или разработка становится очень сложной, а уделять слишком много времени Open Source голодный жилудок не дает.

Ну вообще оно так, но не так)
У наличия финансирования опенсорс проекта есть и плюсы и минусы.
Плюсы:


  • Разработчикам хватает на покушать
  • Есть реклама проекта

Минусы:


  • В текущее время ситуация такая: один проект поддерживает одна компания, и тут, соответственно, появляется суровое влияние этой компании. То есть появляется диктатура, а это на опенсорсе ооочень плохо сказывается.
  • Если финансирование компанией заканчивается, то проект задыхается в разы быстрее, чем проект, который вообще не имел финансирования. Потому что проекту самому приходится культивировать всё то, что за него делала компания, а если точнее, то люди из компании, специально обученные для этого.
Речь идёт о богатых компаниях, типа google, facebook. К примеру, вот взяли они разработчика redux к себе и теперь он то и дело занимается им, а так же самим react'ом и все вроде бы хорошо.
Мне кажется диктатура не может быть такой — «А давай те нагадим всем, и сделаем react медленным!!!1»
Они в первую очередь переживают за проект, хотят дать ему больше возможностей и предлагают то что полезно проекту, это финансирование, а в ответ получают рекламу бренда, немножечко хайпа среди разрабочиков и крутой инструмент, который им самим интересен, ведь они финансируют то что им самим в первую очередь нужно!?

Мне кажется или я слишком наивный? Поправьте меня пожалуйста

Тут просто реклама идёт двусторонняя. Сами подумайте. Нанять разраба, у которого несколько k звёзд на гитхабе — это реклама обоим. А если этот человек с несколькими k звёзд делает крутую технологию, то тут уже будет вообще рекламища похлеще всякой 8 900 55… Потому что разраб "работает на крутых людей, которые не дадут спуску", а компания "финансирует такой крутой проект, который вообще-то опенсорсовский".

а это на опенсорсе ооочень плохо сказывается.

Приведите пример успешного open-source продукта где нет диктатуры или жесткой политики внесения изменений?

curl, wc…
Давайте сначала определимся что значит "успеный продукт" и "жёсткая политика внесения изменений".

Автор пароноик, что плохого в том, что за определенной технологией стоит целая компания?

В современном программировании существует довольно жесткая конкуренция между технологиями. Поддержка корпорацией своей внутренней разработки может создать видимость того, что эта технология хорошая и вообще кому-то нужна. Из сразу вспомнившегося — GWT. Из современных примеров по-видимому второй Ангуляр.
Будут внедрять нужные компании фитчи, в конкретные компании, а не универсальные механизмы для сообщества. Но это не обязательно.
Когда-то верилось с трудом, ведь слова что в ru сегменте вся информация устаревшая, казались неправдой, такого просто не могло быть, ведь я и так успевал с трудом. А теперь я реально знаю что скоро, когда angular будет уже в прошлом, у нас скажут что пришла пора учить angular! Два года назад заминусовали бы за упоминание react рядом с typescript, который тогда считали чем-то ужасным. Печалит лишь тот факт, что когда начнут говорить о том что из реакта ну не как нельзя собрать полноценный angular, второй уже тоже будет на полках истории. Попробуйте angular и Вы поймете что всю свою жизнь мечтали именно о нем.
Прямо сейчас в работе несколько месяцев проект на angular 2-4 после 2х лет реакта. Сказать, что впечатление негативное — не сказать ничего.
А у меня много примеров, когда выходило наоборот — от реакта плевались. Мне кажется, каждый инструмент хорош по-своему и не надо тратить время на бесконечные холивары.
Вспомните свои слова ещё через два года.
И холиваров быть не может, angular максимально на сегодняшний день реализует компонентный подход в декларативном стиле. Просто нужно ещё много лет чтобы люди это поняли, приняли, подождали ещё чуть-чуть и только потом, когда как им покажется все утихлось, они начнут говорить что, да, вот angular действительно хорош. Два года назад реактовцов считали ненормальной прослойкой и пророчили успехи ember и meteor, а ts называли очередным coffee. Сегодня все кардинально поменялось и как человек наблюдающий за этим много лет могу сказать что существует завтра будет так как скажут об этом те, кто зарабатывает на уроках деньги. А делают они это специально медленно выжимая все соки и тем самым останавливая развитие ru сегмента. И это нормально, у каждого своя дорога. Лично я с первых дней люблю и реакт и ts точно так же как полюбил angular, в то время как от первого отказался сразу же из-за его несуразной идеологии угодить всем и сразу.
максимально на сегодняшний день реализует компонентный подход в декларативном стиле
Компоненты там — смех один. Ни HoC не сделать, ни отнаследоваться нормально, ни расширить другой, так как нет ни рендеринга в качестве хоста, ни возможности пробросить пропсы.
Декларативный стиль — это когда для рендеринга попапа надо руками вставить разметку в body? Или когда ngComponentOutlet выбрасывает событие в котором нужно руками проставить аутлету все inputs/outputs? Или то же самое, связанное с ContentChildren, ViewChild, TemplateRef? Ну смешно просто.

Инкапсуляция стилей — смех один. Привязка класснеймов к аттрибуту не изолирует компонент от возможного влияния снаружи через этот класснейм.
Из-за того, что компонент не импортирует другой при рендеринге, а берет из модуля, ломается граф зависимостей и, как следствие, порядок стилей в выходном css.

Можно долго продолжать, мне просто непонятно, с чего вдруг такой хайп вокруг недоделанного фреймворка с откровенно неудачным дизайном.

PS. Я это все не холивара ради, а чтобы указать на реальные существующие проблемы.
не понял насчет разметки в body для попапа. Кто Вам запрещает написать директиву на которая этот темплейт вставит в ?
Не обращайте внимания, людям которые начали учить программирование с математики очень сложно понять что отображение, это геометрия, а связи, это символика и лишь алгоритмы, это алгебра. Человек который много лет учится и достигает результатов решать первое и второе с помощью третьего может никогда не понять что есть другие более удачные подходы. Программисты ооп понимают это и там где нужно писать в функциональном стиле, делают это, а там где не нужно, они пишут на чем-то другом. И что странно, ооп программисты понимают все парадигмы, а функциональщике не могут.
Вы в своих рассуждениях как-то так легко и невзначай упускаете такой момент, что не все бросаются в крайности, и у этих «функциональщиков, которые не могут» может быть приличный ооп-бэкграунд.

Я вам не про парадигмы писал, и вообще не хочу уходить в эти холивары. Я лишь опровергаю ваше крайне необоснованное утверждение о компонентах и декларативности там, где ничего этого нет.
Дело в том что много-много лет назад я велся на такие вот предположения, но потом спустя много лет видел, как те кто это предположения предполагал, доказывали своими поступками что они не относятся к тем, о ком говорили. И такое видел много раз и как правило это были люди, которые занимались, как не странно, образованием. Каждое поколение программистом повторяется одна и та же картина и я уже не помню на какой итерации решил остановится считать, ведь понял, что они говорят не со зла и не о себе или ком-то, а об абстрактных представлениях.
Лично Вы назвали проблемы, которые я даже не понимаю как можно создать! То есть у Вас мышление работает как-то совсем по другому. И это не плохо, возможно даже Вы бы смогли быть крутым писателем компиляторов или драйверов, то есть в тех областях где нужна математика. Но я против когда в архитектуру тащат алгебру. Это вот как представьте что к Вам придет слесарь-диетолог и скажет что у Вас пора в раковке от пробки встала, а возможно сосед от переизбытка калорий начал изливаться салом находясь в омывальне и его секреции бляшками пристыли к водовой системе. И ещё бы он пожаловался что ему трудно работать зная что существует шунтирование. Кто бы он был по Вашему, глупец или хвастун?
Каждое поколение программистом повторяется одна и та же картина и я уже не помню на какой итерации решил остановится считать
Поведуете несведающим об опыте печальном?

Лично Вы назвали проблемы, которые я даже не понимаю как можно создать! То есть у Вас мышление работает как-то совсем по другому.
Вы не понимаете, что такое HoC, и для чего они делаются? Или не понимаете, зачем нужна инкапсуляция стилей? Не понимаете как порядок импортов поверх вебпака влияет на порядок стилей в css?

Но я против когда в архитектуру тащат алгебру.
Простите, но о чем вы вообще? Какая алгебра?

Ну а уж пример ваш… браво
Конечно, несколько лет пишу не пойми что и не понимаю не о стилях не о чем-то другом.
Но зато, у меня нет Ваших проблем. А алгебра, это та которой Вам в унылом es6 не хватает о чем Вы ниже вчера поведали.
ну то есть когда у нас данные в куче с операторами над ними определенными (ООП, классы и объекты) — то это кавайно и кошерно, а когда типы ДАННЫХ содержат только данные а операторы вынесены в алгебру — то это не по феншую? Ну ок.
О как вы здорово с «алгебры в архитектуре» на «алгебру в es6» перескакиваете!
Смотрите, если у вас этих проблем нет, я за вас только рад, пусть их и дальше не будет. А я уж буду с горем пополам набираться опыта и, надеюсь, меня не постигнет участь этих несчастных:
те кто это предположения предполагал, доказывали своими поступками что они не относятся к тем, о ком говорили. И такое видел много раз и как правило это были люди, которые занимались, как не странно, образованием. Каждое поколение программистом повторяется одна и та же картина и я уже не помню на какой итерации решил остановится считать, ведь понял, что они говорят не со зла и не о себе или ком-то, а об абстрактных представлениях
Ну я уверен что архитектура приложений Вы строите как «тут лямбда, тут функтор, а тут будет пару монад и тысячу оберток и все смешаем в общую кучу и пусть доступ будет везде и ещё раз тысячу оберток».
Вы совершенно не имеете представления о том, что такое «лямбды, функторы и монады», раз думаете, что эти вещи относятся к архитектуре. Я бы рекомендовал вам ознакомиться с хорошей книжкой, причем на примере языка для клиент-сайда.
хватит говорит о калбэкаде, давайте поговорим о аде из оберток и о конструкциях типа — а я делаю вот так —
var friend = (((props ||{}).user ||{}).friends[0] ||{}).friend;

ну ты вообще лох, нужно вот так —
var friend = null;
props && props.user && props.user.friends && props.user.friends[0] && /* !!! */ friend = props.user.friends[0].friend;

Только начинали радоваться что jQ уходит в прошлое, на тебе промисы плохо, es6 просто унынье и давайте все омрачим идрисом.
У вас что, от идриса так, эммм, «наболело»? Или я как-то оскорбил ваши es6-чувства? Мы ж вроде в этой ветке вообще по другому поводу?

Вы так хорошо меня "процитировали". Может представите свой способ решения таковой задачи без "ада"? Может тоже свой велосипед накатали? Или всё же пользуетесь элвис-оператором?

Мне такой "велосипед" нравится — https://lodash.com/docs/4.17.4#get


var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// => 3

_.get(object, ['a', '0', 'b', 'c']);
// => 3

_.get(object, 'a.b.c', 'default');
// => 'default'

Оригинальности 0… Это слишком не интересный велосипед, которым пользуются все, если в проекте лодаш есть. В андерскор тоже вроде есть _.get, но я андерскором не пользуюсь.

Впервые мы такой велосипед сами написали еще несколько лет назад (идея достаточно банальная), потом переключились на метод из lodash. Возможно, где-то в другом языке подглядели, не помню уже.

Да я тоже писал аналог, только я писал немного по-другому, у меня разделители были другие, было что-то типа такого:


gIfIs(object, 'a#0.b.c', 0);

Но потом я нашёл underscore, потом lodash, а потом перестал работать с тем, что может себя так повести, а где и работаю, то просто не использую метод get из lodash, хотя вот сейчас понимаю, что я и так подключаю его из-за find, а гет из него выдернуть тоже можно бы...


Надо будет переписать кое-что...

Меня в данный момент напрягает, что _.get получает все свойства, даже если они не собственные. Логичней было бы разделить на _.get и _.getIn (по аналогии с _.has и _.hasIn).


_.has([], 'forEach')
> false

_.hasIn([], 'forEach')
> true

_.get([], 'forEach')
> function forEach() { [native code] }
Никто не запрещает, более того, они уже есть — ngComponentOutlet и ngTemplateOutlet. Но ни одна из этих директив не поддерживает контекст (автоматическую, а главное декларативную, простановку нужных свойств), и, чтобы достичь нужного результата, нужно возвращаться к императивщине и распихивать руками нужные значения в нужные inputs.

Посыл был не в том, что это не работает, а в том, что это не декларативно.
Я знаю точно, что у меня нет проблем с реактом и у меня нет проблем с angular. Как Вы проектируете и о чем говорите я тоже не понимаю. Я желаю людям развиваться, а Вы не использовать angualr.
Да согласен, иногда приходится писать больше чем хотелось бы… По моему только в реакт можно писать что-то типа:
return <Component {...this.props} />


Я так понимаю основная проблема в этом? В реализации декоратора. Правильно?
Именно! Одна проблема — отсутствие механизма задания inputs/outputs скопом (spread), вторая — что у меня всегда будет ненужная обертка вокруг <Component/>
Сказать, что впечатление негативное — не сказать ничего.

А по факту, в чем лично для вас фундаментальные различия? Попробуйте подойти с позиции "что похоже" и "что совсем по другому".

пропсы в хоки не прокинишь и попапы нужно самому руками в боди вставлять! Это же после стольких лет сделать такую жесть даже гуглу не подсилу. Просто представьте что по мнению автора там за чудовище из каменного века получилось :) Но лучше перестать спорить, а то только представить что в angular будут советовать отказываться от сервисов в пользу свалить все в одну кучу.
пропсы в хоки не прокинишь

делаем отдельный компонент, экспоузим сервис который уже можно инджектить куда надо и вроде как не вижу фундаметальных различий.


Ну то есть, вы и так и так должны в своем компоненте получить возможность как-то "вставить попап в боди". Разница минимальна.

Подход, который предлагается официально — либо вставить TemplateRef (надо сначала найти в своей разметке) во ViewContainerRef, либо класс компонента через ComponentFactoryResolver в такой же ViewContainerRef. Чуть более декларативное решение предлагается через ngComponentOutlet и ngTemplateOutlet.

Да, это все доступно, и да, проблема решаема. Но большим отличием является то, что мне в любом случае нужно прибегать к императивным вызовам для простановки пропсов в создаваемые динамически компоненты. «Вставить попап в боди» — это утрирование, вы же понимаете, что проблема глубже.
Фундаментально меня не устраивает попытка команды ангуляра усидеть на двух стульях — то есть «мы как бы все из себя такие ооп» (я ничего не имею против ооп), но еще и за реактом хотим успеть, мы хотим компоненты, декларативный ui и вот это все. Если бы они не гнались за композицией в интерфейсе, то все бы было хорошо. Наверное, это отвечает на оба вопроса — и похоже и совсем по-другому.
Я чувствую, что меня насильно пытаются втянуть в бесполезное набрасывание на вентилятор. Это тема как минимум уже изъзжена вдоль и поперек и ничего нового я вам не расскажу.
За реактом? Хотя бы сказали за c#, java, а то реакт, это как-то мелко слишком. Вспомните себя через пару лет когда пойдут статьи «ангулар это как реакт только круче», «как я полюбил ангулар» и поругайте себя за то что два дня пишите.
Ох, правда, шарп с джавой? «ангулар это как реакт только круче»? Вы вообще понимаете, о чем пишете? :)

Окей… хорошо.


мы хотим компоненты, декларативный ui и вот это все.

Представьте на секундочку что компоненты это объекты. И бац, мы можем делать композицию объектов! И бац, объектно-ориентированный код вполне может быть декларативным! И это не говоря уже о том что идея UI как композиция компонентов настолько не нова… Подобные подходы использовались практически во всех UI фреймворках на десктопах.


усидеть на двух стульях

вы про объекты vs функции? опять же, что если я скажу что между OO и FP принципиальной разницы… нет. Ну то есть если убрать эти аксиомы в духе "все есть объект" и "все есть функции" и сделать "есть объекты и функции" то в целом это ложится на обе парадигмы просто прекрасно. А далее уже нюансы языков программирования и системы типов.


Наверное, это отвечает на оба вопроса — и похоже и совсем по-другому.

Я вижу разницу лишь в том где происходит дерти чекинг. У ангуляра между стэйтом и вьюхой, а реакта между вьюхой и реальным DOM. Соответственно профит между тем и другим виден исходя из того чего у нас больше — стэйта или мутации DOM.


Причем многие все равно готовку стэйта предпочитают отделять от вьюх (что бы проще тестировать, что логично). А если рассматривать декларативные вьюхи и вьюхи как чистые функции — вот хоть убейте не вижу фундаментальной разницы.


Это тема как минимум уже изъзжена вдоль и поперек и ничего нового я вам не расскажу.

Согласен, но я все же считаю что все это настолько похоже друг на друга...

Представьте на секундочку что компоненты это объекты. И бац, мы можем делать композицию объектов! И бац, объектно-ориентированный код вполне может быть декларативным!
Это все здорово! И именно под этим соусом подается в офф-доках. Но, представьте, что есть у вас одна кнопка с каким-то интерфейсом взаимодействия и со шрифтом в 16px. И вам надо из нее сделать точно такую же кнопку но со шрифтом в 14px. Как это сделать без копипасты интерфейса? Создать класснейм, сложить его в глобал и вставлять везде? Ну так себе. Директива тоже не спасет, так как в нее нельзя заинкапсулировать стили. Отнаследоваться от кнопки (вообще это первое, что приходит в голову в ооп-движке)? Но нет, метаданные декоратора не наследуются, так что вам придется вручную скопипастить всю конфигурацию декоратора.

OO и FP принципиальной разницы
Разница в том, что в случае с ООП, сцена дается как данное окружение, и в этой сцене я рисую кнопку, задаю ей параметры, тут рисую инпут, задаю параметры. ФП — сцена, это такая сцена, в которой есть кнопка с определенными свойствами на определенном месте вместе с инпутом с определенными свойствами в определенном месте. Вот она декларативность, чувствуете разницу? Собственно, это и относится к разнице. И тот и тот подход годится, но только по отдельности, а не в каше.
Как это сделать без копипасты интерфейса?

Для начала зададимся вопросом. Вот этот модифицированный элемент UI — маленькая кнопка, это внешний мир знает или сама кнопка? Ну мол мы должны из контекста менять стили или мы лишь задаем контекст кнопке а она уже сама знает как себя рендрить?


Пишем в стилях кнопки:


:host-context(.btn-small) {
    font-size: 14px;
}

и используем наш компонент:


<my-btn (click)="doAction()" class="btn-small">My Button</my-btn>

тем самым мы повышаем реюз компонента. При этом не сильно нарушая open-close принцип.


кнопка с определенными свойствами на определенном месте вместе с инпутом с определенными свойствами в определенном месте.

Вообще-то вы сейчас обрисовали то как это должно быть в объектом мире. Просто куча акторов которые могут обмениваться сообщениями. Но координаты эктора это его личное дело а не сцены.

Это внешний мир знает, базовая кнопка не должна знать, что она где-то может использоваться с каким-то флагом, причем менять свое поведение, подстраиваясь под этот флаг. Если расширение не ограничивается лишь размером шрифта, а например включает изменение поведения, это тоже все упаковывать в базовую кнопку?
Ваш пример почти похож на правду, если бы можно было сложить это дело в отдельный компонент my-small-btn, а класс btn-small лежал бы в модуле my-small-btn. Но так не сделать, так как вокруг my-btn будет обертка my-small-btn.

Просто куча акторов которые могут обмениваться сообщениями.
Различие в том, как вы их создаете.
//OOP
const scene = new Scene();
const button = new Button();
button.content = 'hi'; //imperative
scene.add(button); //imperative
const input = new Input();
input.value = '123'; //imperative
scene.add(input); //imperative

//FP
const scene = Scene([ //declarative
  Button({ //declarative
    content: 'hi'
  }),
  Input({ //declarative
    value: '123'
  })
]);


Можно достичь такой же декларативности и в ооп, и она, собственно, и достигается в ангуляре. Только вот она превращается в тыкву, когда вам нужно, например, кнопку создать в сторонке так же декларативно в текущем контексте, а потом так же декларативно отрендерить. И тут начинается всякое непотребство из серии ComponentFactoryResolver и компании.
Различие в том, как вы их создаете.

Давайте думать. Я не очень хочу на каждый цикл перерисовки генерить вот этот вот код, потому скорее всего я захочу выстроить композицию функций. То есть скорее всего у меня будет одна функция Scene которая является вершиной графа. Я хочу вызвать одну функцию, передать туда весь стэйт и он сам раскидается по нижлежащим функциями и они уже отрендрят кусочки вьюхи.


Так что по сути мы будем иметь что-то типа:


const render = compose(
     Button,
     Input    
);

scene(state); // вычисляет весь DOM для сцены

А теперь вспоминаем что в jS функции это таки объекты. И что мы на самом деле сделали — это композицию имутабельных объектов, по сути не имеющих своего стэйта. Их задача — взять стэйт и продьюснуть DOM.


А то что вы сделали мутабельные объекты да еще и инкапсуляцию сломали к чертям… ну это ваш осознанный выбор и он не имеет ничего общего с классическим ОО подходом. Это больше похоже на C++ way, который больше про структурное программирование нежели actor model.

Ради бога, через compose, через children — одни и те же яйца.

У меня складывается впечатление, что мы о разных вещах говорим. Либо я слишком туманно выражаюсь, либо что-то не понимаю в ваших высказываниях, либо и то, и то вместе.

Ну являются функции объектами в js, ну свели вы их в композицию — прекрасно. Я вам не о невозможности этого пытаюсь донести, а о подходе к «донастройке» функций (окей, объектов, компонентов, как угодно) уже находящихся в композиции в angular 2. Делается это достаточно костыльно и недекларативно, с чего все это обсуждение вообще началось.
Я крайне не хочу вдаваться в дебри различий ооп и фп, изначальный посыл был совсем другой.
Вы штаны через голову надеваете. ComponentFactoryResolver — низкоуровневое апи, что-то вроде рефлексии, для этих вещей оно не предназначено. Для аналога HOC (а это анти-паттерн, само по себе, и в Реакте используется только из-за бедности) есть ng-content (бывший трансклюд), доступ к текущему контексту у него есть по дефолту, а внутренний скоп компоненты инжектится через template reference variable. Это в том случае, если требуются «полноценные» HOC, для простого подмешивания поведения используются директивы или наследование (в зависимости от того, что и как надо подмешать).
ComponentFactoryResolver — низкоуровневое апи, что-то вроде рефлексии, для этих вещей оно не предназначено.
Да что вы говорите

а это анти-паттерн, само по себе, и в Реакте используется только из-за бедности
Да что вы говорите

есть ng-content
И каким же образом вас ng-content спасет от лишней обертки? Даже если у вас весь темплейт это <ng-content><ng-content/>?

а внутренний скоп компоненты инжектится через template reference variable
Ну так я и пишу, нужно руками распихать все input/outputs в этот референс. Где тут декларативность?

для простого подмешивания поведения используются директивы или наследование (в зависимости от того, что и как надо подмешать).
Через директиву вы не подмешаете стили. Через наследование вы не наследуете конфигурацию декоратора — придется копипастить.
> Да что вы говорите

Именно то, что написано по указанной ссылке. Вы же ее прочли?

> Да что вы говорите

Не надо путать HOF и HOC. С точки зрения домена компонент не является ф-ей (у него, как минимум, есть еще внутреннее состояние и метаданные). При этом HOC — как раз обычная функция. Название «Higher-order component» удобно, но по факту не является корректным.

> И каким же образом вас ng-content спасет от лишней обертки? Даже если у вас весь темплейт это <ng-content><ng-content/>?

От какой обертки?

> Ну так я и пишу, нужно руками распихать все input/outputs в этот референс. Где тут декларативность?

Здесь есть несколько моментов:
1. Упомянутые выше HOF вы как вызываете? Точно так же распихивая аргументы руками, менее декларативными они от этого не становятся, правда?
2. За показанный выше проброс аргументов через spread надо вырывать руки.
3. Он возможен только в том случае, когда у вас аргументы чисто случайно совпадают — то есть, практически никогда.

> Через директиву вы не подмешаете стили.

Потому что стили — это не поведение, все верно.

> Через наследование вы не наследуете конфигурацию декоратора — придется копипастить.

Во-первых, конфигурация компонента — это три строчки, так что если бы копипаста и была, то это не помешало бы совершенно никак. Но ведь ее на самом деле и нет, вот в чем штука. Что именно вы копипастите? Стиль? Так вы его переопределили. Селектор? Он тоже другой. Только темплейт остается, да и тот не всегда.
Именно то, что написано по указанной ссылке. Вы же ее прочли?
Ну тогда ткните меня носом в место, где написано, что это low-level и что не используется для динамического создания компонентов.

Не надо путать HOF и HOC. С точки зрения домена компонент не является ф-ей (у него, как минимум, есть еще внутреннее состояние и метаданные).
Это вот, простите, почему? У компонента прекрасно может и не быть состояния. И чем тогда он будет являться? Ничем, кроме как функцией от inputs.

От какой обертки?
@Component({
	selector: 'my-foo',
	template: '<ng-content></ng-content>'
})
class Foo {
}

@Component({
	selector: 'enhanced-foo',
	template: '<my-foo>enhanced</my-foo>'
})
class EnhancedFoo {
}

//markup
//<enhanced-foo><my-foo>enhanced</my-foo></enhanced-foo>


Точно так же распихивая аргументы руками, менее декларативными они от этого не становятся, правда?
Вы что, правда не видите разницы?
@Component({
	selector: 'my-foo',
	template: '<ng-container #container></ng-container>'
})
class Foo implements AfterViewInit {
	@ViewChild('container', {
		read: ViewContainerRef
	})
	containerRef: ViewContainerRef;

	@Input()
	ComponentClass: Type<{
		someField: any
	}>;

	constructor(private componentFactoryResolver: ComponentFactoryResolver) {
	}

	ngAfterViewInit() {
		const factory = this.componentFactoryResolver.resolveComponentFactory(this.ComponentClass);
		const componentRef = this.containerRef.createComponent(factory);
		const {instance} = componentRef;
		instance.someField = '123'; //Это что, декларативно?
	}
}

//или
const MyFoo = ({ComponentClass}) => (
	<div>
		<ComponentClass someField={123}/>
	</div>	
);


За показанный выше проброс аргументов через spread надо вырывать руки
Это ваше личное мнение, и как аргумент я бы вас попросил его не приводить.

Он возможен только в том случае, когда у вас аргументы чисто случайно совпадают — то есть, практически никогда.
Не понимаю, о чем вы. Если вам нужно что-то вырезать из аргументов — spread operator, если добавить — добавляйте доп. аргументами.

Во-первых, конфигурация компонента — это три строчки, так что если бы копипаста и была, то это не помешало бы совершенно никак
Простите, но на аргумент как-то тоже не тянет. Что за поблажка, раз 3 строчки — значит можно копипастить? А то, что компонент должен быть blackbox'ом вас не смущает? Стили я, может, хочу добавить, расширить базовые. Темплейт я, может, тоже как-то хочу переиспользовать. И вы предлагаете прописывать в расширяющем компоненте пути до частей реализации базового компонента? Ну что-то с трудом тянет на решение.

> Ну тогда ткните меня носом в место, где написано, что это low-level и что не используется для динамического создания компонентов.

Для динамического создания компонентов и используется. HOC тут при чем? Слово «Dynamic» я там вижу, слов «Higher-order» — нет. Это вам не говорит ни о чем?

> У компонента прекрасно может и не быть состояния.

Может и не быть, но в общем случае — оно есть.

> Вы что, правда не видите разницы?

Ну так вы продолжаете штаны через голову натягивать.

@Component({
    selector: "foo",
    template: `<div><ng-content></ng-content></div>`,
})
export class FooComponent {
    public data = 123;
}

// <foo #scope><my-component [someField]="scope.data"></foo>


Именно так подобного рода задачи решаются в ангуляре. Естественно, это несколько другой способ, у него по сравнению с реактовскими HOC есть как недостатки (более вербозен), так и достоинства (выше гибкость).

А всякого роды ref'ы с фабриками — это либо инструмент для построения сложных УИ по динамических конфигурациям, либо АПИ для structural directives.

> Простите, но на аргумент как-то тоже не тянет. Что за поблажка, раз 3 строчки — значит можно копипастить?

Совсем несложно написать свой вытягивающий метаданные из суперкласса декоратор (это же обычная функция), если вам это прямо надо. Будете вместо Component(...) писать @DerivedFrom(BaseComponent, {… если надо что-то перегрузить..}). То есть это решаемая проблема, и решаемая без каких-то особых затрат.
Для динамического создания компонентов и используется. HOC тут при чем? Слово «Dynamic» я там вижу, слов «Higher-order» — нет. Это вам не говорит ни о чем?
А где вы увидели, что я говорю, что оно используется для создания HOC?

Может и не быть, но в общем случае — оно есть.
И что, он теперь не функция? Более того, компонент со стейтом (даже класс, представьте) — это тоже функция от инпутов, но с сайд-эффектом в виде стейта.

Ну так вы продолжаете штаны через голову натягивать.
Ну а вы продолжаете чушь нести. Окей, я упрощу вам задачу. Вот есть кнопка my-button. Сделайте такую же кнопку my-button-with-icon, но с доп. инпутом icon, которая будет эту иконку вставлять слева от ng-content. Интерфейс my-button-with-icon должен быть ровно таким же, как и у my-button. (Давайте для интереса, какой-нибудь output назначим). Покажите, как вы это через ng-content без лишних оберток сделаете.

Совсем несложно написать свой вытягивающий метаданные из суперкласса декоратор (это же обычная функция), если вам это прямо надо.
Вот он, воистину, angular-way! Вместо обычного class extend мне нужно пилить кастомный декоратор? Класс.
> И что, он теперь не функция? Более того, компонент со стейтом (даже класс, представьте) — это тоже функция от инпутов, но с сайд-эффектом в виде стейта.

Нет, не функция. Компоненты реакта не являются чистыми функциями, они обладают состоянием (как «внешним», то есть данные используемые при рендеринге компоненты, так и «внутренним» — состояние компоненты, изменяющееся при помощи апи-хуков и по ходу lyfecycle), которое, кстати, не является иммутабельным. А «ф-я от инпутов» — это render.

> Покажите, как вы это через ng-content без лишних оберток сделаете.

Зачем через ng-content? Это же не HOC, сделаю через наследование:
@Component({
    selector: "my-button",
    template: `<button (click)="clicked.emit()">{{ name }}</button>`,
})
export class MyButtonComponent {
    @Input() name: string;
    @Output() clicked = new EventEmitter;
}

@Component({
    selector: "my-button-with-icon",
    template: `<span>{{ icon }}</span><my-button (clicked)="clicked.emit()" [name]="name"></my-button>`,
})
export class MyButtonWithIconComponent extends MyButtonComponent {
    @Input() icon: string;
}

@Component({
    selector: "test-button",
    template: `<my-button-with-icon [name]="'name'" [icon]="'icon'" (clicked)="click()"></my-button-with-icon>`,
})
export class TestButtonComponent {
    public click() {
        console.log("Clicked!");
    }
}


Но если уж хочется через ng-content, то можно так:

@Component({
    selector: "my-button",
    template: `<button (click)="clicked.emit()">{{ name }}</button>`,
})
export class MyButtonComponent {
    @Input() name: string;
    @Output() clicked = new EventEmitter;
}

@Component({
    selector: "with-icon",
    template: `<span>{{ icon }}</span><ng-content></ng-content>`,
})
export class WithIconComponent {
    @Input() icon: string;
}

@Component({
    selector: "test-button",
    template: `<with-icon [icon]="'icon'"><my-button [name]="'name'" (clicked)="click()"></my-button><with-icon>`,
})
export class TestButtonComponent {
    public click() {
        console.log("Clicked!");
    }
}


В обоих случаях все вполне себе декларативно, нет?

> без лишних оберток

Какое-то надуманное требование. Но если хотите без оберток — можете сделать себе структурную директиву, которая будет делать unwrap вашего компонента, не вижу здесь какой-либо проблемы

> Вот он, воистину, angular-way! Вместо обычного class extend мне нужно пилить кастомный декоратор? Класс.

Декоратор — это обычная функция. В чем проблема написать обычную функцию на 10 строк для реализации своих хотелок? Почему в реакте вы так делаете и не бухтите, а в ангуляре — нельзя?
Компоненты реакта не являются чистыми функциями
Вы бы ради приличия в доки хоть заглянули

сделаю через наследование
Уже же вроде обсудили, не наследуется конфигурация.

<with-icon [icon]="'icon'"><my-button [name]="'name'" (clicked)=«click()»></my-button><with-icon>
И в чем прикол? Каждый раз таскать за собой эту конструкцию? Вы лукавите, так как задачу не решили — при попытке спрятать этот темплейт в компонент, вам придется руками прокидывать все возможные inputs/outputs до кнопки. Именно это я вам и пытался донести.

Какое-то надуманное требование
Требование совсем не надуманное, так как некоторые компоненты могут быть ячейками таблицы, на некоторых может стоять height: 100% и т.п. Я опережу вас и сразу скажу, что аргумент «не делать такие компоненты», потому-что «ангуляр не может», не катит. Если вы скажете использовать директивы, то я отвечу, нафига вообще эти компоненты в ангуляре.

Но если хотите без оберток — можете сделать себе структурную директиву, которая будет делать unwrap вашего компонента, не вижу здесь какой-либо проблемы
Каждый раз ее таскать за собой? Зачем вообще вся эта возня с компонентами, если такая логика не инкапсулируется?

Декоратор — это обычная функция. В чем проблема написать обычную функцию на 10 строк для реализации своих хотелок? Почему в реакте вы так делаете и не бухтите, а в ангуляре — нельзя?
И на каждом компоненте ее вызывать вместе с обычными наследованием, чтобы унаследовать еще какой-то кусок? Каких хотелок? Я ожидаю от инструмента корректной работы со стандартными языковыми конструкциями, такими как, о боги, обычное наследование, а не подмены их своими кривоработающими велосипедами. Вы что, правда считаете, что это нормально? Тут товарищ TheShock любит напоминать про Стокгольмский синдром.

И в каком месте я это делаю в реакте?
const Foo = props => <div>foo</div>;
class Bar extends React.Component {
	render() {
		return (
			<div>bar</div>
		);
	}
}


> Вы бы ради приличия в доки хоть заглянули

У вас с логикой какие-то явные проблемы. Из того, что существуют негры, которые являются людьми, следует ли, что люди — это негры? Нет. Точно так же и из того, что существуют функции, которые являются компонентами, не следует, что компоненты — это функции.

> Уже же вроде обсудили, не наследуется конфигурация.

Вам никто не мешает наследовать, если хотите.

> при попытке спрятать этот темплейт в компонент, вам придется руками прокидывать все возможные inputs/outputs до кнопки.

Вы же таскаете с собой эту конструкцию каждый раз, когда дергаете фунарги? Или вы в выборе из двух вариантов:
function f(x: number, y: number) {
  return x+y;
}

function g1(z: number, x: number, y: number) {
  return f(x, y) + z;
}

function g2(z: number, ...args: number[]) {
  return f.apply(args) + z;
}

вы выбираете g2? Мы тут не договоримся, потому что по моему мнению такой код не должен проходить ревью.

> Каждый раз ее таскать за собой? Зачем вообще вся эта возня с компонентами, если такая логика не инкапсулируется?

Ну в реакте же вы будете это все таскать за собой? Да и в любой другой библиотеке. Какой-либо функционал, которого нет в библиотеке, вы реализуете сами и таскаете за собой. Например, если надо будет наоборот оборачивать все компоненты во врапперы, то вы что сделаете?

> И на каждом компоненте ее вызывать вместе с обычными наследованием, чтобы унаследовать еще какой-то кусок?

И в чем проблема?

> И в каком месте я это делаю в реакте?

Вы пишете в реакте обычные функции, которые реализуют функционал, которого в реакте нет. Почему вы не можете поступить так же в ангуляре?

> Я ожидаю от инструмента корректной работы со стандартными языковыми конструкциями, такими как, о боги, обычное наследование, а не подмены их своими кривоработающими велосипедами.

Он и работает корректно и ожидаемо, а ваши «хотелки» состоят в том, чтобы «было как в реакте». Это уже вопрос религиозных убеждений и обсуждать его я смысла не вижу. Вы привели выше конкретную и объективную претензию — отсутствие декларативности. И, действительно, всякие вьюрефы ей не отличаются. Я выше привел пример, как это делается без вьюрефов, с оверхедом не выше, чем в реакте. Вас это решение по уровню декларативности устраивает?
У вас с логикой какие-то явные проблемы. Из того, что существуют негры, которые являются людьми, следует ли, что люди — это негры? Нет. Точно так же и из того, что существуют функции, которые являются компонентами, не следует, что компоненты — это функции.
Ну раз у вас с логикой все в порядке, поведайте миру, чем принципиально HOF отличается от HOC.

Вам никто не мешает наследовать, если хотите.
Сторонними средствами?

Вы же таскаете с собой эту конструкцию каждый раз, когда дергаете фунарги?
Вы что продать пытаетесь? Что разработчик в каждом месте должен писать <with-icon><my-button></my-button></with-icon> вместо <my-button-with-icon></my-button-with-icon>? Бред. То, что вы это делаете — еще не значит, что это верно.
Ну в реакте же вы будете это все таскать за собой?
Аналогично — вместо простого компонента, нужно помнить, что нужно везде лепить эту директиву. В реакте это упаковывется в компонент и используется без заморочек, что тут надо класс подоткнуть, тут директиву, а тут кастомную разметку.

И в чем проблема?
С тем, что это излишнее усложнение. Разработчик при экстенде должно помнить, что нужно влепить еще и кастомный декоратор. Зачем?

Вы пишете в реакте обычные функции, которые реализуют функционал, которого в реакте нет. Почему вы не можете поступить так же в ангуляре?
Ау. Вы о чем? Я про отсутствие левых декораторов и врапперов. Реакт использует языковые конструкции вместо велосипедов.

а ваши «хотелки» состоят в том, чтобы «было как в реакте»
Мои хотелки состоят в том, чтобы было как в языке. Без лишних сущностей. Стандартными средствами.

Я выше привел пример, как это делается без вьюрефов, с оверхедом не выше, чем в реакте.
Вы все-равно не видите разницу? Уложите <with-icon> и <my-button> в один компонент без наследования.

Я вам не пытаюсь продать «не использовать ангуляр», а вы мне не пытайтесь продать «использовать ангуляр». Иначе разговор зайдет в тупик.
Я вам описал проблему, вы проблему не решили. А только привели сомнительные примеры, как с помощью дополнительных конструкций и повышения общей сложности достичь чего-то похожего на решение.
> Ну раз у вас с логикой все в порядке, поведайте миру, чем принципиально HOF отличается от HOC.

Тем, что HOF — это ф-я, которая принимает и возвращает ф-ю, а HOС — это компонент, который принимает и возвращает компонент. Да, реактовские HOС не являются полноценными, я об этом выше уже говорил.

> Сторонними средствами?

Почему сторонними? Вполне нативными.

> Вы что продать пытаетесь? Что разработчик в каждом месте должен писать <with-icon><my-button></my-button></with-icon> вместо <my-button-with-icon></my-button-with-icon>?

Ну так сделайте как в реакте, в чем проблема? Объявите компоненту <my-button-with-icon></my-button-with-icon>, которая будет раскрываться в <with-icon><my-button></my-button></with-icon>. Почему вы не используете реактовские подходы?

> В реакте это упаковывется в компонент и используется без заморочек, что тут надо класс подоткнуть, тут директиву, а тут кастомную разметку.

Потмоу что в реакте это поведение по умолчанию. Если вы захотите какое-то другое поведение — то будет то же самое подтыкание, то тут, то там. В ангуляре другое поведение по умолчанию. Во фреймворке Х — какое-то еще, третье. Это нормально.

> С тем, что это излишнее усложнение.

Вызвать ф-ю — это усложнение? Вы программист, вообще?
У меня, кстати, такой вопрос — а вы используете редакс? Явно нет. Он ведь целиком и полностью состоит из подобных «усложнений», не правда ли?

> Ау. Вы о чем? Я про отсутствие левых декораторов и врапперов.

Функции (они же декораторы) — входят в библиотеку Vanilla JS. Поддерживаются искаробки на всех браузерах и всех устройствах, не требуют импорта, настройки и чего бы то ни было. Какие проблемы? Почему вы используете функции в реакте, но не хотите их использовать в ангуляре?

> Стандартными средствами.

Функции — стандартные средства языка.

> Вы все-равно не видите разницу?

Я вижу разницу, конечно же. В одном случае мы забиваем гвоздь молотком, в другом — вворачиваем шуруп отверткой. Результат практически тот же.

> Уложите <with-icon> и <my-button> в один компонент без наследования.

Почему без наследования-то? :cry:

> Я вам описал проблему, вы проблему не решили.

Вы сказали, что решение на ангуляре недекларативно, я показал декларативное решение. То, что там немного не такой синтаксис как в реакте (или в любом другом фреймворке Х) — ну так это понятно, я здесь не вижу проблемы. Обсуждать религиозные вопросы, еще раз повторю, незачем. Вот был вопрос про декларативность, давайте обсуждать его, а претензии к синтаксису и прочим несущественным вещам — в сторону.

> как с помощью дополнительных конструкций и повышения общей сложности достичь чего-то похожего на решение.

Да я секрет открою, вы можете вообще переопределить Component и импортировать его потом из своего ../utils вместо angular/core. И никто вообще разницы не заметит.

Я, конечно, не предлагаю это как реальное решение — но просто если вам хочется странного такого, то ради бога.
Тем, что HOF — это ф-я, которая принимает и возвращает ф-ю, а HOС — это компонент, который принимает и возвращает компонент.
Ну и что? Что теперь-то? Абстракция ровно одна и та же.
Да, реактовские HOС не являются полноценными, я об этом выше уже говорил
Только вот непонятно, почему. И еще не понятно, с чего вы взяли, что это антипаттерн.

Почему сторонними? Вполне нативными.
Кастомный декоратор — это сторонние средства. Обычное наследование классов без бубнов — нативные.

Ну так сделайте как в реакте, в чем проблема? Объявите компоненту <my-button-with-icon></my-button-with-icon>, которая будет раскрываться в <with-icon><my-button></my-button></with-icon>.
Да потому-что придется копипастить inputs/outputs из my-button в my-button-with-icon.

Почему вы не используете реактовские подходы?
Потому-что они не работают в из-за сильной ограниченности «компонентов» в ангуляре.

Вызвать ф-ю — это усложнение? Вы программист, вообще?
У меня, кстати, такой вопрос — а вы используете редакс? Явно нет. Он ведь целиком и полностью состоит из подобных «усложнений», не правда ли?
Вызвать лишнюю функцию — это усложнение. У вас наверное весь код состоит из ненужной лапши, подтыкающей кривую архитектуру/дизайн. Не поверите — использую и очень много. Еще больше не поверите, использую ngrx/store — что почти то же самое. И это все не вызывает недоумения при использовании, так как все по делу.

Функции (они же декораторы) — входят в библиотеку Vanilla JS. Поддерживаются искаробки на всех браузерах и всех устройствах, не требуют импорта, настройки и чего бы то ни было. Какие проблемы? Почему вы используете функции в реакте, но не хотите их использовать в ангуляре?
Вы на другом уровне абстракции застряли, поднимитесь. На кой черт мне какие-то обертки для наследования, если язык уже предоставляет инструмент? Та же фигня с этими NgModule — зачем мне какие-то левые конструкции, если язык уже предоставляет стандартное решение? Повторюсь, воистину, angular-way.

Функции — стандартные средства языка.
Аналогично.

Я вижу разницу, конечно же. В одном случае мы забиваем гвоздь молотком, в другом — вворачиваем шуруп отверткой. Результат практически тот же.
У вас есть опыт разработки в команде? Когда приходит новый человек, например? Вы пишите для таких ситуаций огроменные мануалы, что, когда нужно отрендерить кнопку с иконкой, на самом деле нужно отрендерить два компонента в строгом порядке, один в другом, еще и с директивой какой-нибудь? И делать это нужно каждый раз в каждом участке кода. Вместо того, чтобы взять готовый компонент, который является простейшей композицией обычной кнопки и этой несчастной иконки.

Почему без наследования-то? :cry:
Да потому-что обычное наследование не работает. Дополнительный декоратор — это не решение, а костыль к тому, что не работает наследование.

Вы сказали, что решение на ангуляре недекларативно, я показал декларативное решение. То, что там немного не такой синтаксис как в реакте (или в любом другом фреймворке Х) — ну так это понятно, я здесь не вижу проблемы. Обсуждать религиозные вопросы, еще раз повторю, незачем. Вот был вопрос про декларативность, давайте обсуждать его, а претензии к синтаксису и прочим несущественным вещам — в сторону.
Окей, тут соглашусь

Да я секрет открою, вы можете вообще переопределить Component и импортировать его потом из своего ../utils вместо angular/core. И никто вообще разницы не заметит.
Потрясающе, теперь вся команда должна знать, что стандартный Component использовать нельзя, нужно использовать кастомный. И мы возвращаемся к огромному бессмысленному мануалу.
> Да потому-что придется копипастить inputs/outputs из my-button в my-button-with-icon.

Инпуты/аутпуты наследуются. У меня же выше был пример с наследованием, там это видно.

> Кастомный декоратор — это сторонние средства.

Декоратор — это _просто функция_. Вот это:
@Component({ ...props })
export class FooComponent {
    
};

ровно то же самое, что и вот это:
class Foo {

};

export const FooComponent = Component({ ...props })(Foo);

Когда вы на реакте применяете какую-то ф-ю к компоненту — вы фактически и используете декоратор :)
//так вы пишите:
const EnhancedComponent = higherOrderComponent(WrappedComponent);

//а можно записать с синтаксисом декоратора:
@higherOrderComponent
export class WrappedComponent {
    
} 

то есть «определить и использовать в реакте HOC» и «определить и использовать кастомный декоратор» — это _одно и то же_. HOC из реакта — это _и есть_ декораторы классов.

> что стандартный Component использовать нельзя, нужно использовать кастомный. И мы возвращаемся к огромному бессмысленному мануалу.

Команда же может запомнить, что надо юзать higherOrderComponent выше? :)

> Окей, тут соглашусь

Значит, по факту декларативности мы вопрос утрясли.
Инпуты/аутпуты наследуются. У меня же выше был пример с наследованием, там это видно.
Мы ходим по кругу.

«определить и использовать в реакте HOC» и «определить и использовать кастомный декоратор» — это _одно и то же_. HOC из реакта — это _и есть_ декораторы классов.
Это все очень и очень здорово. Только вот, чтобы получить кнопку с иконкой, ни декораторы, ни HOC не используются:
const Button = props => {
	const {children, onClick} = props;
	return (
		<button onClick={onClick}>{children}</button>
	);
};

const ButtonWithIcon = props => {
	const {icon, children, ...rest} = props;
	return (
		<Button {...rest}>
			<Icon name={icon}/>
			{children}
		</Button>
	);
};


HOC в виде декоратора используется для других целей.
Занимательно, что можно и ButtonWithIcon превратить в HOC, просто принимая компонент Icon через props:
const Button = props => {
	const {children, onClick} = props;
	return (
		<button onClick={onClick}>{children}</button>
	);
};

const ButtonWithIcon = props => {
	const {icon, children, Icon, ...rest} = props;
	return (
		<Button {...rest}>
			<Icon name={icon}/>
			{children}
		</Button>
	);
};


Ну вот что может быть удобней и проще? Никаких декораторов, костылей и подпорок — просто функции. Более того, во всех этих кнопках сохраняется стандартный браузерный интерфейс кнопки — всякие там tabindex и т.п.
Вот момент про ButtonWithIcon в качестве HOC не читайте, что-то я погорячился. Всмысле терминологически
> Никаких декораторов, костылей и подпорок — просто функции.

Декоратор — и есть просто функция. Мы, вроде, уже это выяснили?

> Это все очень и очень здорово. Только вот, чтобы получить кнопку с иконкой, ни декораторы, ни HOC не используются:

ну вот мой вариант был:
@Component({
    selector: "my-button",
    template: `<button (click)="clicked.emit()">{{ name }}</button>`,
})
export class MyButtonComponent {
    @Input() name: string;
    @Output() clicked = new EventEmitter;
}

@Component({
    selector: "my-button-with-icon",
    template: `<span>{{ icon }}</span><my-button (clicked)="clicked.emit()" [name]="name"></my-button>`,
})
export class MyButtonWithIconComponent extends MyButtonComponent {
    @Input() icon: string;
}

то же самое ведь?

> Мы ходим по кругу.

Нет, они сами по себе наследуются. Без «кастомных декораторов» и всего такого:
export class MyButtonWithIconComponent extends MyButtonComponent {
    @Input() icon: string;
}
Декоратор — и есть просто функция. Мы, вроде, уже это выяснили?
Это и выяснять не надо было. Разговор был о целесообразности, а не о классификации.

то же самое ведь?
Совсем не то же самое. Иконка должна быть внутри my-button. И даже если запихнуть span внутрь my-button в темплейте my-button-with-icon, у вас появляются ненужные обертки (возвращаемся к проблеме наличия оберток). Кроме того, если у кнопки есть стили, а в 99% случаев они есть, то они не наследуются. Был заведен тикет, но по традиции его быстро закрыли.

Более того, если вы в базовой кнопке добавили input/output — вам нужно пойти во все наследники и руками пробросить это все в шаблоне (как в случае с пробросом clicked).

Нет, они сами по себе наследуются. Без «кастомных декораторов» и всего такого. См. выше.
По кругу мы ходим из-за проблем ненаследования метаданных, а не наследования inputs/outputs.
> Это и выяснять не надо было. Разговор был о целесообразности, а не о классификации.

Ну вы согласны тогда, что писать свои метаредьюсеры нецелесообразно? И hoc в реакте — тоже? Если да, то тогда ваша позиция понятна. Если нет — то тогда тут наблюдается явная непоследовательность.

> По кругу мы ходим из-за проблем ненаследования метаданных, а не наследования inputs/outputs.

Тогда к чему вы приплели inputs/outputs?

> вам нужно пойти во все наследники и руками пробросить это все в шаблоне (как в случае с пробросом clicked).

Это сродни претензии к тому, что если вы в функцию добавили аргумент, то надо везде, где эта функция вызывается, добавить этот аргумент. Я тут ничего плохого не вижу.

> Кроме того, если у кнопки есть стили, а в 99% случаев они есть, то они не наследуются.

Так это правильно. Они легко могут сломаться, если их отнаследовать.

> Совсем не то же самое. Иконка должна быть внутри my-button.

Так бы сразу и сказали, я же не могу мысли читать:
@Component({
    selector: "my-button",
    template: `<button (click)="clicked.emit()"><ng-content></ng-content></button>`,
})
export class MyButtonComponent {
    @Input() name: string;
    @Output() clicked = new EventEmitter;
}

@Component({
    selector: "my-button-with-icon",
    template: `<my-button (clicked)="clicked.emit()" [name]="name"><span>{{ icon }}</span></my-button>`,
})
export class MyButtonWithIconComponent extends MyButtonComponent {
    @Input() icon: string;
}

так?
Ну вы согласны тогда, что писать свои метаредьюсеры нецелесообразно? И hoc в реакте — тоже? Если да, то тогда ваша позиция понятна. Если нет — то тогда тут наблюдается явная непоследовательность.
Вы в упор меня не слышите, и складывается впечатление, что специально. Целесообразно использовать инструмент для решения задачи, но нецелесообразно лепить костыли для решения проблемы в дизайне. Вот что мешало включить всю конфигурацию декоратора в тело класса? Желание быть похожими на джаву с аттрибутами? Мнимая «переиспользуемость» классов отдельно от этих декораторов? Я вообще ни разу не видел класс отдельно от декоратора. Зачем вообще тогда эти декораторы? Создали бы интерфейс IComponent и своим чудо-шаблонизатором проверяли бы все используемые в шаблоне компоненты.

Тогда к чему вы приплели inputs/outputs?
Это вы их приплели. Я вам про то, что метаданные не наследуются и наследование не подходит — вы мне про то, что inputs/outputs наследуются, так что можно использовать наследование. Я вам про то, что метаданные не наследуются, ну и так далее…

Это сродни претензии к тому, что если вы в функцию добавили аргумент, то надо везде, где эта функция вызывается, добавить этот аргумент. Я тут ничего плохого не вижу.
Нет, не сродни. Мы говорим о наследовании, а не о композиции. Если ButtonWithIcon наследует Button, то подразумевается что все поведение, вплоть до работы с EventEmitter наследуется, а не копипастится. Иначе грош цена такому наследованию, и нужно использовать композицию. А с рендерингом на уровне хоста без обертки у ангуляра проблемы.

Так это правильно. Они легко могут сломаться, если их отнаследовать.
Прикольно. Вот наследуете вы кнопку, добавляете какие-нибудь хуки туда, метаданные не трогаете. И все, новая кнопка без стилей и маркапа.

Так бы сразу и сказали, я же не могу мысли читать:
Не притворяйтесь, вы прекрасно поняли задачу. И из самой постановки и из примера на реакте. И проблему этот новый пример все-равно не решил.

Вот чего припираемся-то? Мой посыл в том, что дизайн кривой, пользоваться неудобно, проблемы есть. Вы пытаетесь меня переубедить или что? Что дизайн оправдан? Нет. Что пользоваться удобно? Нет. Проблем нет? Хм, нет.
> Вы в упор меня не слышите, и складывается впечатление, что специально. Целесообразно использовать инструмент для решения задачи, но нецелесообразно лепить костыли для решения проблемы в дизайне.

То что мы обсуждаем — и есть обычное использование инструмента для решения задачи. Мне непонятно, почему вы готовы писать обычные функции в реакте, но против того, чтобы делать то же самое в ангуляре в точно том же самом контексте.

> Вот что мешало включить всю конфигурацию декоратора в тело класса?

То, что это просто нельзя сделать сколько-нибудь осмысленным образом. Те метаданные, что имеет смысл наследовать, наследуются. А те, что наследовать нельзя — не наследуются. Ни стили, ни темплейты, ни другие подобные вещи наследовать просто нельзя, это чревато серьезными проблемами. То есть, эту фичу невозможно сделать так, чтобы она не была заведомо поломанной.

> Мнимая «переиспользуемость» классов отдельно от этих декораторов?

Вот, кстати, да. Если желаете — можете вынести конфигурацию декоратора и переиспользовать ее в разных классах (наследниках, например). И никаких «кастомных декораторов».

> Не притворяйтесь, вы прекрасно поняли задачу.

Нет, не понял. Ваш пример на реакте был уже позже, я когда его увидел — то переделал.

> Мой посыл в том, что дизайн кривой, пользоваться неудобно, проблемы есть.

Удобство — это субъективная категория. Я не вижу какой-либо разницы по сравнению с реактом. Мне, например, jsx не удобен и кривое разделение ответственности (ну тут, собственно, проблема в том, что реакт — не фреймворк, а библиотека, и для нормальной работы так же как и редакс предполагается дорабатывать его напильником, так что все так и должно быть).

> Нет, не сродни.

Это ваше мнение.

> Мы говорим о наследовании, а не о композиции. Если ButtonWithIcon наследует Button, то подразумевается что все поведение, вплоть до работы с EventEmitter наследуется, а не копипастится.

Во-первых, оно и наследуется. Не наследуется шаблон, потому что его нельзя осмысленно наследовать, а ивентэмиттеры и т.п. — наследуются, я же об этом выше уже говорил. Во-вторых, нет, это именно композиция. Вы пишите одну компоненту, в которой вызывается другая компонента, естественно вы передаете во вторую компоненты требуемые ею аргументы, точно так же как вызывая функцию из другой функции. Это происходит на уровне controller/view (шаблона). А на уровне модели (ComponentClass) вы наследуете. В реакте модель гвоздями прибита к вью (их можно разделить, вообще, но сам реакт по дефолту не о том), в ангуляре — это две разные и независимые сущности. По-этому наследуются метаданные которые относятся к модели (соответствующие декораторам членов), но не наследуются те, что относятся к виду Это же простая и ясная структура. В чем проблема?

> Что дизайн оправдан? Нет.

Это ваше мнение. Единственную вашу объективную претензию мы рассмотрели, если других таких же нету — я не вижу смысла продолжать. Потому что «здесь вызов функции — использование инструмента, а там — костыль» или «оно врапает а я хочу чтобы не врапало» — это не разговор.
То что мы обсуждаем — и есть обычное использование инструмента для решения задачи. Мне непонятно, почему вы готовы писать обычные функции в реакте, но против того, чтобы делать то же самое в ангуляре в точно том же самом контексте.
@Component({
	selector: 'my-button',
	style: ':host { background: red }',
	template: '<button></button>'
})
class Button {
}

@InheritMetadata()
@Component({
	selector: 'my-custom-button',
	style: ':host { background: green }'
})
class CustomButton extends Button {}

Вы считаете целесообразным лепить InheritMetadata в каждом компоненте, чтобы помочь наследованию сделать дело? Если да, то пора заканчивать этот разговор, к согласию мы не придем.
То, что это просто нельзя сделать сколько-нибудь осмысленным образом.
Что мешает template, style и компанию складывать в class properties? B вся компания дружно унаследуется. И работать с ними можно будет стандартными средствами языка.
Те метаданные, что имеет смысл наследовать, наследуются. А те, что наследовать нельзя — не наследуются.
Не наследуются все метаданные.
Ни стили, ни темплейты, ни другие подобные вещи наследовать просто нельзя, это чревато серьезными проблемами.
В каком-то вы странном мире живете. Поясните, какими проблемами? Наследуя компонент кнопку я не могу унаследовать цвет ее фона?
То есть, эту фичу невозможно сделать так, чтобы она не была заведомо поломанной.
Зачем вообще тогда использовать декораторы? Выстроили бы обычную иерархию для наследования, как везде делали много лет.
Вот, кстати, да. Если желаете — можете вынести конфигурацию декоратора и переиспользовать ее в разных классах (наследниках, например). И никаких «кастомных декораторов».
Вот вы упертый. Вместо наследования теперь нужно грузить какие-то доп. вещи из компонента. Это все-равно костыль. Плюс возвращаемся к первому пункту.
Удобство — это субъективная категория.
Ну то есть про дизайн и проблемы вы согласны? Давайте резюмировать уже
Не наследуется шаблон, потому что его нельзя осмысленно наследовать
Святая корова, да почему?? Шаблон все-равно компайлится в рантайме, какая разница к какой модели (ComponentClass) он будет привязан — к родителю или к потомку? Если возникнет ошибка — то она просто возникнет и все.
Потому что «здесь вызов функции — использование инструмента, а там — костыль» или «оно врапает а я хочу чтобы не врапало» — это не разговор
Ну если вы не видите, в чем заключается проблема наличия «врапает», хотя это уже объяснялось, то это совсем бесполезный разговор. Вы не можете решить проблему, так как она не решаема, либо решаема но с костылями, что вы никак усвоить не можете.
я не вижу смысла продолжать
На этом закончим.
> Вы считаете целесообразным лепить InheritMetadata в каждом компоненте, чтобы помочь наследованию сделать дело?

В реакте все постоянно так делают и никаких проблем это не вызывает. Это во-первых. Во-вторых — метаданные из Component наследоваться и не должны, так что нет, _я_ не предлагаю. Это _вы_ предлагаете такое поведение. Я вам лишь указал, как вы можете его достичь — это делается абсолютно бесплатно, переопределением декоратора.

> Что мешает template, style и компанию складывать в class properties?

Ничего не мешает. Но это не будет работать.

> Не наследуются все метаданные.

А зависимости, видать, инжектятся в потомках за счет святого духа? А инпуты /оутпуты базового класса откуда берутся? Или вы не в курсе, что это все — тоже метаданные?

> Ну то есть про дизайн и проблемы вы согласны?

Вы же пока не назвали проблем.

> Святая корова, да почему??

Потому что поведение будет неопределено. И определить его каким-либо осмысленным образом нельзя. Вы как-то пропустили мой основной тезис из предыдущего поста. Вид и модель — это две разные сущности. Наследуя класс компонента — вы наследуете модель. А вид — он отдельно, он вообще не класс, даже.

Вот у вас в реакте модели как бы нет, есть только вид (и потому он же — модель). Вы пишите свой компонент, пишите — потом обнаруживаете, что в нем слишком много логики. Вы выносите эту логику со всей работой с состоянием в отдельный класс — теперь у вас есть модель и есть вид, при этом модель ничего не знает о виде, и если вы отнаследуете модель — то не получите нового компонента. Точно так же, вы не получаете нового компонента при наследовании модели в ангуляре. И _не должны_.

Далее вы хотите сделать все более молодежно и модно. По SOLID. У вас ведь вид привязан к модели — а это нехорошо. Вы их развязываете — теперь у вас вид зависит не от модели, а от ее интерфейса, а конкретная модель инжектится. А какая модель? Неизвестно — теперь вид ничего не знает о конкретной модели. Значит, нужна конфигурация, и в ней у вас будет написано что-то вроде: «модель Х рендерится видом Y». И если вы хотите отнаследовать одну компоненту от другой — то это сразу не заработает, вам надо пойти и добавить соответствующее вхождение в конфиг, чтобы связать вид с моделью. Как в ангуляре.

Потом проходит некоторое время и оказывается, что ваша конфигурация превратилась в огромную портянку. Да и вообще — прыгать туда-сюда, чтобы узнать что там к чему инжектится и с чем связано — не совсем удобно. Вы распиливаете вашу конфигурацию на куски — по куску на модель — и каждый кусок, соответствующий конфигурации модели, храните вместе с моделью. А конфигуратор потом собирает из этих кусков исходный полный конфиг. И у вас получается архитектура ангуляра. В ангуляре все вышеобозначенные шаги сделаны — потому что это фреймворк. У него есть архитектура. Реакт — это библиотека, в ней архитектуры нет. Предполагается, что ее надо создавать самому для конкретного приложения. И когда вы ее создадите, то точно так же у вас вид будет отделен от модели и будет отдельно наследоваться…
Спасибо за дельное разъяснение, вот сразу бы так, теперь понятно. Но, мне как человеку, пришедшему из реакта, не шибко понятна цель этой возни с ненаследуемыми шаблонами, прибиваемыми к наследуемым моделям. Какая-то архитектура ради архитектуры. Смотрите следующую цитату:
и если вы отнаследуете модель — то не получите нового компонента.
И когда вы ее создадите, то точно так же у вас вид будет отделен от модели и будет отдельно наследоваться
Но если я в реакте «наследую» (расширяю через враппер со спредом, но без лишней дом-ноды) «вид» (компонент), то я получу новый компонент. Со старой моделью. Или с новой — это уже как решу, модель будет приниматься прекрасно через props, либо браться дефолтная. С TS можно еще и ее интерфейс проверить. Видите, к чему я? Почему меня заведомо лишают этой гибкости?

Вы же пока не назвали проблем.
Проблему я назвал — мне нужно расширить кнопку (окей, компонент строки таблицы) без доп. обертки
из коробки, так как, забавно, есть с чем сравнивать. Я же из этих сравнений исхожу, а не с потолка беру такие требования.
> Но если я в реакте «наследую» (расширяю через враппер со спредом, но без лишней дом-ноды) «вид» (компонент), то я получу новый компонент. Со старой моделью.

Ну кто вас какой гибкости лишает? Вы точно так же можете создать новый вид и связать его с расширением старой модели (с добавленным property «icon»), именно это и происходит в моем примере выше. Просто в реакте вы не делаете второй шаг (не описываете связывание), так как модель не выделена, связывать нечего, не с чем, а потому и не нужно. В реакте у вас есть возможность определить компоненту, как единую сущность. В ангуляре — нет. В ангуляре всегда есть разделение на вид и модель и всегда нужно описать их связь. И вы _можете_ сделать так, чтобы эта связь формировалась определенным образом автоматически (@ComponentInherit ваш), но это не является дефолтным поведением.

> Проблему я назвал — мне нужно расширить кнопку (окей, компонент строки таблицы) без доп. обертки

Ну а если я скажу, что мне надо тоже сделать кнопку, но с оберткой? Будут костыли уже в реакте. Я согласен, что иногда (весьма редко) действительно надо без оберток. Тогда компонент применяется атрибут-селектором и все, так что проблема полностью надуманна.
Ну а если я скажу, что мне надо тоже сделать кнопку, но с оберткой? Будут костыли уже в реакте.
Прекрасно — декларативно добавьте свою обертку в разметку. Это же не костыль. Костыль — это unwrap в директиве.
Тогда компонент применяется атрибут-селектором и все, так что проблема полностью надуманна.
Судя по офф-докам, так делать не стоит. Кроме того, использовать компонент по аттрибуту на уже существующем компоненте ангуляр не дает из-за конфликта имен. Директива не подходит из-за стилей. Получается двояко — для обычных (нативных) компонентов нужно использовать аттрибут-селекторы, для ненативных — директивы. Так себе, но окей. Но остается последний вариант — когда нужно расширить кастомный компонент без обертки.
> Прекрасно — декларативно добавьте свою обертку в разметку. Это же не костыль.

Это мне для каждой директивы обертку добавлять? :D
;)

> Судя по офф-докам, так делать не стоит. Кроме того, использовать компонент по аттрибуту на уже существующем компоненте ангуляр не дает из-за конфликта имен.

А все просто — это используется в тех случаях, когда нам _обязательно_ надо избавиться от враппера, по-этому рекомендации из офф-доков не работают. И в этих случаях нам надо раскрывать компоненту в существующий тег — а значит он не будет другой компонентой :)

> Но остается последний вариант — когда нужно расширить кастомный компонент без обертки.

Если у нас кастомный компонент, то тогда нет проблем его обертывать.
Это мне для каждой директивы обертку добавлять? :D
;)
Вы на что намекаете? Что удалять лишний враппер — это ок. А добавить — нет?

тогда нет проблем его обертывать.
Увы, есть. Например есть сетка, внутри которой сидят виджеты. Виджетам проставлена высота 100%, чтобы они растягивались по высоте ячеек сетки. Тут появляется необходимость заменить виджеты контейнерами, которые могут сами подгружать данные. И тут мы идем в контейнер, лепим ему стили, лепим туда детали реализации компонента, который он оборачивает, а именно высоту 100%. Нет, мне конечно не сложно прописать там везде dispay: block; height: 100% — но это же нарушает инкапсуляцию компонента.
Да, я не спорю, можно контейнер сделать в виде директивы. Но почему-то мне это кажется не решением, а уходом от проблемы, когда в виде компонента, в силу неведомых ограничений движка контейнер не сделать. Опять же, это все навеяно уже работающими подходами в реакте.
> Вы на что намекаете? Что удалять лишний враппер — это ок. А добавить — нет?

Это я намекая на ваши претензии вида: «это мне на каждую компоненту @Inheritance ставить? оО».

> Увы, есть.

Не совсем понял ваше объяснение. :host селектор проблему не решает?
Это я намекая на ваши претензии вида: «это мне на каждую компоненту @Inheritance ставить? оО».
Вы не видите разницы? Печаль.

Не совсем понял ваше объяснение. :host селектор проблему не решает?
Решает, я к тому и веду, в контейнере пишем эти стили в :host. Но только выходит, чтобы заработал компонент, в контейнере мне нужно знать как именно его починить — добавить высоту. Тогда как контейнер не отвечает за ui.
> Тогда как контейнер не отвечает за ui.

Если он за ui не отвечает, то он и разметки содержать не должен, а значит — это attribute directive, а не component:
> Attribute directives—change the appearance or _behavior_ of an element, component, or another directive.
Директива? Вы забыли про height: 100%?
То есть, он все-таки отвечает за уи?
Отвечает из-за корявости ангуляра — тадааам — отсутствия HOC. Приходится ручками рендерить компонент в разметке контейнера и напихивать ему нужные инпуты, попутно этом же контейнере разбирая стримы из стора.
И не надо тут петь, что раз уж минимально отвечает, значит надо лепить туда стили. Стили в контейнере — это костыль для работоспособности компонента.
Я не понял. И так, у вас есть компонент, у компонента какие-то пропсы. Вы хотите сделать в точности такой же компонент с таким же видом, но чтобы в нем не было пропсов, а он сам подгружал данные (с сервера, редаксом, еще как -либо — не важно)? Все так? Тогда делаете attribute directive и выставляете инпуты хоста через @HostBinding, нет?
Да, я не спорю, можно контейнер сделать в виде директивы. Но почему-то мне это кажется не решением, а уходом от проблемы, когда в виде компонента, в силу неведомых ограничений движка контейнер не сделать. Опять же, это все навеяно уже работающими подходами в реакте.


UPD: Кстати, а HostBinding поддерживает async pipe?
> Но почему-то мне это кажется не решением

Ну это странное мнение, учитывая что attribute directives предназначены _именно_ для решения таких проблем. То есть вот он инструмент специально для этого заточенный.

> UPD: Кстати, а HostBinding поддерживает async pipe?

В каком смысле? Пайпы же находятся в темплейте. Если вы хотите сделать так, чтобы какоето проперти, которое использовалось без асинка, начало использоваться как бы с асинком — то насколько я знаю, так нельзя, надо делать subscribe и менять обычное значение. Но если оно и так уже было с асинком — то вы можете засунуть туда observable.
В каком смысле? Пайпы же находятся в темплейте.
Ну по аналогии. Да, именно это и хочется — чтобы HostBinding сам распаковывал Observable. Компоненты не знают про входящие стримы. Не шибко удобно конечно руками подписываться на стримы, пихать руками значения в поля, потом еще руками отписываться от этих подписок.
Надо просто во все инпуты, где пробрасываются именно данные (а не константы/конфиги) делать по дефолту обсерваблами (и, с-но, асинк в темплейте). Это даже не то что для таких вот случаев, а в принципе архитектура ангуляра заточена больше под такой flow, он в целом будет удобнее (хотя поначалу и непривычно).
Хм, а действительно ведь.
Вот прямо сейчас пишу код и ловлю себя на мысли, что не первый раз уже Input обратно в Observable упаковываю через ngOnChanges. И каждый раз думаю, ну что за херня-то, ну как так-то.
Druu
Погодите-ка, а как из этой директивы к Output компонента прицепиться-то?
raveclassic и, собственно, вы же можете поступить как в реакте — то есть написать ф-ю, которая принимает один класс (модель), и возвращает новый — с измененным поведением. Вам только надо будет явно связать эту модель со старым видом, и все:
const template = `<div><button>{{ name }}</button></div>`;

@Component({
    selector: "my-button",
    template: template,
})
export class MyButtonComponent {
     @Input() name = "";
}

export function loaded(component: any) {
    return class extends component {
        name = "loaded";
    };
}

@Component({
    selector: "loaded-button",
    template: template,
})
export class LoadedButtonComponent extends loaded(MyButtonComponent) {};
Зачем вообще этот loaded? Можно же просто наследовать.
Плюс у меня есть смутные подозрения, что, выносят темплейт из класса, мы теряем всяческую помощь IDE внутри этого темплейта. А все ради чего? Чтобы угодить дизайну движка, мол, вьюха должна лежать отдельно от компонента. Хотя они блин все-равно тут же связываются воедино. Зачем должна, кому должна, какие это проблемы в реальности решает — не понятно.
> Зачем вообще этот loaded?

Чтобы можно было подмешивать поведение к разным компонентам. А если этого не надо, то вообще не понятно, в чем была исходная проблема.

> Плюс у меня есть смутные подозрения, что, выносят темплейт из класса, мы теряем всяческую помощь IDE внутри этого темплейта.

В тайпскрипте уже сделали language-service, так что это не проблема теперь.

> Зачем должна, кому должна,

Человеческой архитектуре. В реакте-то вы все равно к той же схеме придете, так какая разница.
Человеческой архитектуре. В реакте-то вы все равно к той же схеме придете, так какая разница.
Сохранив при этом компонуемость в том виде, в котором мне нужно. А не с ворохом костылей в угоду «человеческой архитектуре». Я не вижу ни одной причины гоняться за мнимым отделением шаблона от его компонента, тем более, что по отдельности от них все-равно никакой пользы. Точно так же как вы, видимо, не видите смысла в обратном.

Вопрос дизайна и подхода — текущий не дает мне нужной гибкости, которую дает реакт. Я вообще всю эту ветку начал с личного впечатления, но нет, начались какие-то невразумительные попытки описать как простейшие в реакте вещи сделать с огородом костылей в 20 раз сложнее на ангуляре. Зачем? Не пойму.
> Сохранив при этом компонуемость в том виде, в котором мне нужно.

Ну как же вы сохраните? Вид и модель будут отдельно? Будут.

> Я не вижу ни одной причины гоняться за мнимым отделением шаблона от его компонента

А вы в реакте всю логику суете прям в компонент, таким вот невнятным ворохом? Ну тогда ясно все.

> Вопрос дизайна и подхода — текущий не дает мне нужной гибкости, которую дает реакт.

Как не дает, если гибкость решения на ангуляре наоборот _выше_? Все, что можно на реакте — можно легко повторить на ангуляре. Но вот если я начну просить ангуляровские вещи повторить на реакте — у вас это просто вообще не выйдет, тут сработает аналог правила Гринспена :)

> как простейшие в реакте вещи сделать с огородом костылей в 20 раз сложнее на ангуляре

Ну у вас проблема в том, что вы не понимаете архитектуры ангуляра и называете «костылями» пару строчек вполне идеоматического кода. Вы пытаетесь задаунгрейдить ангуляр до примитивного уровня реакта, где просто ничего нет. Это знаете, как сравнивать какой-нибудь ЯВУ с ассемблером. Приходит человек и говорит: «а вот я хочу параметр через стек передать для вызова подпрограммы. В ассемблере пишу две строчки и все работает — push/pop, а как у вас, в java/c#/javascript/etc? Ой какой ужас в 20 раз сложнее». Примерно так для меня ваши тезисы выглядят, если честно.
Ну как же вы сохраните? Вид и модель будут отдельно? Будут.
Компонуемость вида в том виде, в котором она мне нужна — без оберток, наследований, HOC и прочей чепухи.
const Button = ({chilldren}) => (
  <button>{props.children}</button>;
);
const ButtonWithIcon = ({icon, ...props}) => (
  <Button {...props}><Icon name={name}/>{props.children}</Button>;
);
Все. Просто можно выполнять задачу легко, быстро и поддерживаемо, а можно до посинения обмазываться архитектурой.

А вы в реакте всю логику суете прям в компонент, таким вот невнятным ворохом? Ну тогда ясно все.
Если мне понадобиться вынести логику в «модель», я вынесу и буду принимать ее через props. Если нужно вынести еще дальше — есть контекст. А если еще дальше — я поставлю inversifyjs и буду инжектить ее прямо в компонент/контейнер. Все. А можно продолжать обмазываться архитектурой.

Как не дает, если гибкость решения на ангуляре наоборот _выше_? Все, что можно на реакте — можно легко повторить на ангуляре. Но вот если я начну просить ангуляровские вещи повторить на реакте — у вас это просто вообще не выйдет, тут сработает аналог правила Гринспена :)
Ну давайте, жгите!

Ну у вас проблема в том, что вы не понимаете архитектуры ангуляра и называете «костылями» пару строчек вполне идеоматического кода
Проблема у вас, так как вы принимаете откровенно хреновый дизайн за должное. «Вполне» идиоматический код — это код, который несет полезную нагрузку, а не является следствием корявой архитектуры. См. пункт выше — жгите!

Вы пытаетесь задаунгрейдить ангуляр до примитивного уровня реакта, где просто ничего нет
Хахаха, ау! Даунгрейдить ангуляр до ui-библиотеки? Максимум дубовую ui-составляющую. См. пункт выше — жгите!

> Все. Просто можно выполнять задачу легко, быстро и поддерживаемо, а можно до посинения обмазываться никому ненужной архитектурой.

Ну а теперь перепишите это к нормальному виду, для полноценной задачи, чтобы бизнес-логика была вынесена в отдельный класс хотя бы.

> Если мне понадобиться вынести логику в «модель», я вынесу и буду принимать ее через props.

Ну так продемонстрируйте. На однострочниках-то все всегда хорошо :)

> А если еще дальше — я поставлю inversifyjs и буду инжектить ее прямо в компонент/контейнер. Все.

Ну так вы не стесняйтесь, продемонстрируйте то, о чем сейчас так лихо рассказали. Я же не так просто правило Гринспена вспомнил :)

> Ну давайте, жгите!

Давайте с чего-нибудь простого начнем. Сделайте мне аналог компоненты с селектором-атрибутом.
Ну а теперь перепишите это к нормальному виду, для полноценной задачи, чтобы бизнес-логика была вынесена в отдельный класс хотя бы.
Что переписывать-то? Какую логику? Логику установки иконки в кнопке?

Ну так продемонстрируйте. На однострочниках-то все всегда хорошо :)
См. пункт выше.

Ну так вы не стесняйтесь, продемонстрируйте то, о чем сейчас так лихо рассказали. Я же не так просто правило Гринспена вспомнил :)
Ну так а вы не стесняйтесь подкрепить свои заявления делом. Мало того, что вы на ангуляре не пришли к решению задачи с кнопками в таком же виде, как она решена на реакте. Так еще и заявляете, что гибкость библиотек ниже, чем «гибкость» дубового двигла с плохим дизайном.

Сделайте мне аналог компоненты с селектором-атрибутом.
Я не понял, что вы имеет в виду. Можете пример на ангуляре, чтобы понятней было?

> Что переписывать-то? Какую логику? Логику установки иконки в кнопке?

А вы только обертки над кнопками пишете? В реальном мире у большинства компонент есть нетривиальная логика.

> Я не понял, что вы имеет в виду. Можете пример на ангуляре, чтобы понятней было?

Ну attribute directive, мы же о них сейчас как раз говорили.

> Мало того, что вы на ангуляре не пришли к решению задачи с кнопками в таком же виде, как она решена на реакте.

С чего вы взяли, что она должна решаться _в таком же виде_? Главное, чтобы не сложнее, а в таком или не таком — уже не важно.
В реальном мире у большинства компонент есть нетривиальная логика.
Вы про стейт компонента или что? Ну выносите операции над стейтом в функции вида state => state и передавайте их в setState. Когда будете их тестировать, вам даже компонент маунтить не нужно.

Ну attribute directive, мы же о них сейчас как раз говорили.
Аттрибутные директивы введены только как костыль для решения проблемы лишних оберток. Если бы компоненты ангуляра могли рендерить другие в качестве хоста (к слову, как это было в первом), директив бы не было вообще (как в первых RC, если мне не изменяет память).
Если вам нужно доп. поведение — HoC, враппер. Но раз уж просите —
const Component = ({color, children}) => <div style={{color: color}}>{children}</div>;
const HighlightHOC = color => Target => props => <Target color={color} {...props}/>;
const HighlightWrapper = ({color, ...props}) => <Component color={color} {...props}/>;

const HighlightedComponent = HighlightHOC('red')(Component);

const render = props => (
    <div>
        <HighlightedComponent>
            content
        </HighlightedComponent>
        <HighlightWrapper color="red">
            content
        </HighlightWrapper>
    </div>
);
React декларативен, и не позволяет руками «патчить» компоненты из директив, как это делает ангуляр. Когда в то же время достигается совершенно тот же эффект. Но если вам совсем уж надо — есть ref, findDOMNode и все вытекающие.

С чего вы взяли, что она должна решаться _в таком же виде_? Главное, чтобы не сложнее, а в таком или не таком — уже не важно.
С того, что я изначально так ставил задачу.

Главное, чтобы не сложнее
Сравните, пожалуйста, примеры выше еще разок.

> Вы про стейт компонента или что? Ну выносите операции над стейтом в функции вида state => state и передавайте их в setState.

Ну вы, пожалуйста, продемонстрируйте, что получится. А то однострочники меня уже давно не возбуждают, я для этого слишком стар, извините уж.

> Аттрибутные директивы введены только как костыль для решения проблемы лишних оберток.

Все несколько наоборот. Это реакт сам по себе был придуман как костыль для портирования сервер-сайд логики с php на клиент. Оттуда jsx (старательно мимикрирующий в своей семантике под php), оттуда принцип «перерендерить все» (именно это происходит при классическом запросе браузера к серверу — сервер вам генерит каждый раз всю страницу заново), отсюда vdom (чтобы этот подход не слишком сильно тормозил), изоляция стейта да и все остальное.

> Если бы компоненты ангуляра могли рендерить другие в качестве хоста (к слову, как это было в первом)

Так в первом ангуляре тоже были аттрибут-селекторы, несмотря на наличие replace.

> Но раз уж просите —

Вы просто лишний аргумент добавили, я же совсем не то просил. Сделайте аналог аттрибут-директивы, которая будет каждую секунду, например, менять background хоста: черный-красный-черный-красный и т.д.

Чтобы я мог вместо <Component/> написать <Component blink/> — и оно у меня мигает.

> Когда в то же время достигается совершенно тот же эффект.

Так я же процитирую вас:
> к решению задачи с кнопками _в таком же виде, как она решена на реакте_.

Вот и я вас прошу не достичь «такого же эффекта», а решить в том же виде, как на ангуляре. Просто потому, что я «изначально так поставил задачу».

> Но если вам совсем уж надо — есть ref, findDOMNode и все вытекающие.

Ну нет, это не по правилам. Вы же не разрешали писать кастомные декораторы? Значит, никаких refов с findDOMNode — это все ненативно, бяка и т.д., какие вы там аргументы приводили против подобных решений.

> Сравните, пожалуйста, примеры выше еще разок.

Да сравнивал, они практически построчно совпадают (я о варианте с наследованием).
Ну вы, пожалуйста, продемонстрируйте, что получится. А то однострочники меня уже давно не возбуждают, я для этого слишком стар, извините уж.
Ну подкиньте хоть пример, что вам накатать. А то я придумать не могу, у меня в компонентах бизнес-логика не сидит как-то.

Это реакт сам по себе был придуман как костыль для портирования сервер-сайд логики с php на клиент
Просто в слезы! :rofl:

Оттуда jsx (старательно мимикрирующий в своей семантике под php)
Вы до сих пор живете внутри html-шаблона, тогда как jsx !== html. К моему великому сожалению, xml-подобный синтаксис там введен только чтобы облегчить переход вот таких вот html-щиков.

«перерендерить все» (именно это происходит при классическом запросе браузера к серверу
оттуда принцип «перерендерить все»
Принцип этот не оттуда, а от того, что проще выкинуть трекинг зависимостей кусков разметки от кусков модели. state => ui — все. Это более высокий уровень абстракции. То, что в реальности происходят какие-то доп. процессы, чтобы снять нагрузку на многострадальный DOM — это все детали реализации. Отсюда и vdom.

Так в первом ангуляре тоже были аттрибут-селекторы, несмотря на наличие replace.
Да, но директивы можно было делать и по имени тэга, и именно replace я имел в виду.

которая будет каждую секунду, например, менять background хоста: черный-красный-черный-красный
<Component blink/>
Вы не правильно используете синтаксис — интерфейс компонента не объявляет аттрибут blink. Еще раз вернемся к jsx !== html — это более высокий уровень абстракции. Я могу вам на лету собрать мигающий компонент из немигающего — это будет правильно, и по сути тоже самое. Синтаксис другой только. Но это вам не лишняя обертка в DOM, так что все честно.
const Component = ({backgroundColor, children}) => (
    <div style={{backgroundColor: backgroundColor}}>{children}</div>
);

const WithBlink = ({initialColor = 'black'} = {}) => Target => {
    return class Blinking extends React.Component {
        static displayName = `Blinking(${Target.name})`;

        state = {
            color: initialColor
        };
        interval;

        render() {
            return (
                <Target backgroundColor={this.state.color} {...this.props}/>
            );
        }

        componentDidMount() {
            this.interval = setInterval(() => {
                this.setState({
                    color: this.state.color === initialColor ? 'red' : initialColor
                });
            }, 1000);
        }

        componentWillUnmount() {
            clearInterval(this.interval);
        }
    }
}

//static
const AlreadyBlinkingComponent = WithBlink()(Component);

//dynamic
const render = ({backgroundColor = 'green'}) => {
    //on the fly
    const DynamicBlinkingComponent = WithBlink({initialColor: backgroundColor})(Component);

    //some markup
    return (
        <div>
            <DynamicBlinkingComponent>1</DynamicBlinkingComponent>
            <AlreadyBlinkingComponent>2</AlreadyBlinkingComponent>
        </div>
    );
};


<Target backgroundColor={this.state.color} {...this.props}/>

Кстати, я такую запись так и не смог нормально типизировать на TS. Как вы это разруливаете?
Как вы это разруливаете?
В целом как-то так:
import * as React from 'react';

type TComponentCtr<P, S> = {
    new (props?: P, context?: any): React.Component<P, S>
};
type TResult<P, S> = React.ComponentClass<P> & TComponentCtr<P, S>;

function WithBlink() {
    return function decorate<P>(Target: React.ComponentClass<P> | React.SFC<P>): React.ComponentClass<P> {
        return class Blinking extends React.Component<P, never> {
            render() {
                return (
                    <Target {...this.props}/>
                );
            }
        };
    };
}

//

type TInjectedProps = {
    backgroundColor: string
};

type TOwnProps = {
    something: string
};

type TFullProps = TOwnProps & TInjectedProps;
type TProps = TOwnProps & Partial<TInjectedProps>;

const Component: React.SFC<TFullProps> = props => (
    <div>{props.something} {props.backgroundColor}</div>
);

//this is important! though it's not a cast (as)
const Blinking: React.ComponentClass<TProps> = WithBlink()(Component);

const test = <Component/>; //both props are missing
const test2 = <Blinking/>; //only somthing is missing

Согласен, возни супермного, но rest types на подходе: #13470, #12215, #4183, ну и ultimate #13251. Что из этого появится быстрее, остается только гадать, но типизировать HOC станет в разы проще.
Дааамс. Пока легче руками развернуть)
Ну кстати ни TComponentCtr, ни TResult не нужны…
Когда введут rest types, можно будет делать как-то так:
import * as React from 'react';

type TTarget<P> = React.ComponentClass<P> | React.SFC<P>;
type TBlinkingProps = {
    backgroundColor: string
};
function WithBlink() {
    return function decorate<P extends TBlinkingProps, R extends Rest<P, TBlinkingProps> & Partial<TBlinkingProps>>(Target: TTarget<P>): React.ComponentClass<R> {
        return class Blinking extends React.Component<R, never> {
            render() {
                return (
                    <Target backgroundColor="red" {...this.props}/>
                );
            }
        };
    };
}

//

type TProps = TBlinkingProps & {
    something: string
};

const Component: React.SFC<TProps> = props => (
    <div>{props.something} {props.backgroundColor}</div>
);

const Blinking = WithBlink()(Component);

const test = <Component/>; //both props are missing
const test2 = <Blinking/>; //only somthing is missing
> Ну подкиньте хоть пример, что вам накатать. А то я придумать не могу, у меня в компонентах бизнес-логика не сидит как-то.

Ну так а я о чем? Не сидит. Вот вы и покажите мне реальный пример, где бизнес-логика в компонентах не сидит, а не кнопки--однострочники :)

> Вы до сих пор живете внутри html-шаблона, тогда как jsx !== html.

Слушайте, ну на эту маркетинговую чепуху ведутся только те, кто на php ни разу не писал. Называть-то вы это можете как угодно, но суть явления от смены нвзвания не меняется, и jsx выглядит в точности как генерация портянок html коды из пыха, то самое прокидывание пропсов кстати — оно и в php было, там же тоже функциями генерили, render-методы у классов :)

> Принцип этот не оттуда

Именно оттуда :)
Это сделано для того, чтобы логика клиент-сайда максимально напоминала логику сервер-сайда. Именно сервер-сайд у вас работает по принципы state -> view. Ну а вдом — да, деталь реализации, необходимая для того, чтобы «писать на клиенте как на сервере».

> Да, но директивы можно было делать и по имени тэга, и именно replace я имел в виду.

Вы говорите, что директивы-атрибуты нужны как костыль из-за отсутствия replace. В первом ангуляре было и то и то. Что-то не сходится?

> Вы не правильно используете синтаксис — интерфейс компонента не объявляет аттрибут blink.

Я ж вам показываю ангуляроподобный синтаксис. А задача — сделать как в ангуляре, все верно.

> Еще раз вернемся к jsx !== html — это более высокий уровень абстракции.

Да наоборот все. Шаблоны — это ДСЛ, а в jsx вы пишете по факту на хост-языке.

> Я могу вам на лету собрать мигающий компонент из немигающего — это будет правильно, и по сути тоже самое. Синтаксис другой только.

Ну хорошо, пусть другой. Но объявлять отдельно блинкающуюся компоненту я не хочу, я хочу сразу в jsxe как-то это сделать. Как будет ваш код без объявления промежуточного AlreadyBlinkingComponent выглядеть? Что если у меня таких директив не одна, а 10? мне 2^10=1024 таких компонент объявлять? Кроме того, ваша компонента изначально позволяет менять background-color, то есть заточена работать с blinked. Я хочу возможность работы с любой компонентной, даже с той, у которой background-color в пропсах нет.
Ну так а я о чем? Не сидит. Вот вы и покажите мне реальный пример, где бизнес-логика в компонентах не сидит, а не кнопки--однострочники :)
Как мне вам показать пример компонента с бизнес-логикой, которая в нем не сидит? Компонент — state => ui, вся бизнес-логика в сагах, все состояние в редьюсерах.

Слушайте, ну на эту маркетинговую чепуху ведутся только те, кто на php ни разу не писал.
Все понятно

Называть-то вы это можете как угодно, но суть явления от смены нвзвания не меняется, и jsx выглядит в точности как генерация портянок html коды из пыха, то самое прокидывание пропсов кстати — оно и в php было, там же тоже функциями генерили, render-методы у классов :)
Надеюсь, что вы когда-нибудь дойдет до осознания, что jsx — это не разметка страницы, а формальный язык описания интерфейса. А то, что есть выхлоп в html — так это один из возможных вариантов.

В первом ангуляре было и то и то.
В первом ангуляре были только директивы. Которые «выглядели» как компоненты. С сохранением нужной гибкости: не нужна обертка — ставим replace.

А задача — сделать как в ангуляре, все верно.
Вы как дите малое. Я вам про реальную проблему — наличие лишней обертки и возможность решить эту пробему на реакте и невозможность решить ее на ангуляре, а вы за синтаксис.

Да наоборот все. Шаблоны — это ДСЛ, а в jsx вы пишете по факту на хост-языке.
jsx — это не какой-то шаблон, который «живет отдельно от модели», это описание композиции функций — посмотрите выхлоп бабеля в конце концов уже.

я хочу сразу в jsxe как-то это сделать
я тоже много чего хочу

Как будет ваш код без объявления промежуточного AlreadyBlinkingComponent выглядеть?
Я вроде показал — создание Dynamic на лету.

Что если у меня таких директив не одна, а 10? мне 2^10=1024 таких компонент объявлять?
Перечитал раза 3 — не понимаю о чем вы. То, что в рантайме создается что-то? Ну так у вас AlreadyBlinking позволяет принимать тот же пропс. Используйте его.

Я хочу возможность работы с любой компонентной, даже с той, у которой background-color в пропсах нет.
Пффф, и как вы это в директиве сделаете? Либо через @Host с простановкой нужного инпута (а это ровно тот же случай) либо через nativeElement — а это прямой доступ в DOM.

Вы ерунду-то не несите, очевидно же, что задача решена, а вам обидно.

А еще лучше — приведите конкретный пример для переноса на реакт.
> Как мне вам показать пример компонента с бизнес-логикой, которая в нем не сидит?

Ну так в том и дело. Я хочу увидеть, как будет выглядеть ваше решение в полноценном виде, с отдельной моделью.

> Надеюсь, что вы когда-нибудь дойдет до осознания, что jsx — это не разметка страницы, а формальный язык описания интерфейса.

Ну так и HTML — это формальный язык описания интерфейса.

> Вы как дите малое. Я вам про реальную проблему — наличие лишней обертки и возможность решить эту пробему на реакте и невозможность решить ее на ангуляре, а вы за синтаксис.

Так наследованием — все работает. Но вам это не нравится по синтаксическим соображениям.

> В первом ангуляре были только директивы. Которые «выглядели» как компоненты. С сохранением нужной гибкости: не нужна обертка — ставим replace.

Еще раз — вы говорите что раз есть реплейс, то атрибут-директивы не нужны. Но они там были. Почему?

> jsx — это не какой-то шаблон, который «живет отдельно от модели», это описание композиции функций — посмотрите выхлоп бабеля в конце концов уже.

Шаблоны ангуляра — это тоже описание композиции функций. Они компилируются так же как jsx в чистый джаваскрипт, там хтмля не остается. Ну и на пехепе тоже композиции функций писали :)

> Я вроде показал — создание Dynamic на лету.

Ну вы же создаете ее все равно в промежуточном виде. Как сразу в jsx? Или он превратился в тыкву тут?

> Вы ерунду-то не несите, очевидно же, что задача решена, а вам обидно.

Нет, не решена. Сделайте, чтобы работало с любой компонентной.

> А еще лучше — приведите конкретный пример для переноса на реакт.
@Directive({
    selector: "[mixin]",
})
export class MixinDirective implements OnDestroy  {
    @HostBinding("style.backgroundColor") color = "red";

    subscription = Observable.interval(1000)
        .subscribe(x => this.color = this.color === "red" ? "black" : "red");

    ngOnDestroy = () => this.subscription.unsubscribe();
}

Ну так в том и дело. Я хочу увидеть, как будет выглядеть ваше решение в полноценном виде, с отдельной моделью.
Мне вам что, сюда проект скопипастить? Будет выглядеть как в любом примере с сагами. Ну и вы так и не сказали, решение чего.

Еще раз — вы говорите что раз есть реплейс, то атрибут-директивы не нужны. Но они там были. Почему?
Потому что тогда не было концепции компонентов. Были только html-тэги и всевозможные аттрибуты на них. Что, к слову, яростно нарушалось ангуляром, так как по спеке доспускались аттрибуты через data- а не все подряд. Это безобразие и перекочевало в головы пхп-шников, пришедших в первый ангуляр. Ну а дальше вы знаете.

Шаблоны ангуляра — это тоже описание композиции функций.
Прекрасно. Только компоузятся они хуже. Там где ангуляр не вытаскивает, вы пытаетесь решить задачу наследованием. А мы помним про composition over inhertitance.

Ну вы же создаете ее все равно в промежуточном виде. Как сразу в jsx? Или он превратился в тыкву тут?
Вы можете себе представить что-то такое:
function f(a: any, b: any, c:any): void {
}

f(1, 2, 3, blink);
«Промежуточный» вид ничем от вашей хотелки не отличается, так как и то, и то все-равно находится внутри одного скоупа функции рендер. Вы просто живете html-аттрибутами, тогда как компонент не является html-тэгом. У него есть строго описанный интерфейс. В случае с TS, например, вам даже лишний проп запихнуть в компонент не дадут без явного объявления в его интерфейсе.

Нет, не решена. Сделайте, чтобы работало с любой компонентной.
Пффф, и как вы это в директиве сделаете? Либо через Host с простановкой нужного инпута (а это ровно тот же случай) либо через nativeElement — а это прямой доступ в DOM.
Вы ерунду-то не несите, очевидно же, что задача решена, а вам обидно.


@HostBinding(«style.backgroundColor») color = «red»;
:lol: то, что вы это в строку запихнули — не значит, что HostBinding в DOM не лезет. Тогда я не понимаю, чем вам не нравится ref/findDOMNode. Ну и вы нарушаете инкапсуляцию компонента, подразумевая что он имеет интерфейс DOM-ноды. И выглядит это как monkey-patching. А почему? А потому что ангуляр застрял в html-шаблоне.

> Мне вам что, сюда проект скопипастить?

Зачем проект? Просто пример.

> Ну и вы так и не сказали, решение чего.

Так пусть будет с теми же кнопками. Просто структурировано так, как это будет в реальности, на нормальных компонентах, с выделенной моделью.

> Это безобразие и перекочевало в головы пхп-шников, пришедших в первый ангуляр

Вот в ангуляре как раз в отличии от реакта от пхп ничего не было :)
То же двухсторонний биндинг — сугубо антипхпшная концепция.

> А мы помним про composition over inhertitance.

В этой имеется ввиду фразе не та композиция, которая функциональная.

> Вы можете себе представить что-то такое:

Вот хороший пример. В случае с ф-и я могу ф-ю прямо в коде использовать, без промежуточного объявления.

> Тогда я не понимаю, чем вам не нравится ref/findDOMNode.

Ну хорошо, пусть будет с рефом. Главное, чтобы работало.
Просто пример.
Какой пример? Как передать в компонент «модель»? Ну держите:
type TModel = {
	name: Observable<string>
};

type TProps = {
	model: TModel
};

type TState = {
	name: string
};

class Person extends React.Component<TProps, TState> {
	private unsubscribe = new Subject<never>();

	componentWillMount() {
		this.props.model.name.takeUntil(this.unsubscribe).subscribe(name => {
			this.setState({
				name
			});
		});
	}

	componentWillUnmount() {
		this.unsubscribe.next();
		this.unsubscribe.complete();
	}

	render() {
		const {name} = this.state;
		return (
			<div>Hi, {name}</div>
		);
	}
}
Но вы же понимаете, что это антипаттерн в случае реакта?
Смотрите, я трюк покажу, как элегантно выпилить возможную логику из компонента:
// Model.ts
type TModel = {
	color: string
};

export const create = (): TModel => ({
	color: 'black'
});

export const changeColor = (model: TModel): TModel => ({
	color: model.color === 'black' ? 'red' : 'black'
});

// Component.ts
import * as Model from './Model.ts'

export class Component extends React.Component<never, TModel> {
	state: TModel = Model.create();
		
	render() {
		const style = {
			backgroundColor: this.state.color
		};
		
		return (
			<div style={style}>
				<button onClick={this.onClick}>change</button>
			</div>	
		);
	}
	
	onClick = () => {
		this.setState(Model.changeColor);
	}
}

И тестируете вы отдельно взятые функции create и changeColor — проще простого, без окружения и без моков.
Просто структурировано так, как это будет в реальности, на нормальных компонентах, с выделенной моделью
В реальности этой «модели» вообще не будет — кнопка это props => ui, какая модель? Props — вот ваша модель, просто без стейта. Если у вас в компонентах сидят какие-то сложные выделенные сущности со сложным стейтом — вы что-то делаете не так.

Вот в ангуляре как раз в отличии от реакта от пхп ничего не было :) То же двухсторонний биндинг — сугубо антипхпшная концепция.
Лихо вы байндингами прикрылись. То, что в реакте их нет, а в ангуляре есть, не делает из первого пхп, а из второго нет.

В этой имеется ввиду фразе не та композиция, которая функциональная
Я аж поперхнулся. Естественно я вам не про функциональную композицию.

Вот хороший пример. В случае с ф-и я могу ф-ю прямо в коде использовать, без промежуточного объявления.
Что, простите? Поясните

Ну хорошо, пусть будет с рефом. Главное, чтобы работало.

const WithBlink = ({initialColor = 'black'} = {}) => Target => {
    return class Blinking extends React.Component {
        static displayName = `Blinking(${Target.name})`;

        color: initialColor;
        interval;

        render() {
            return (
                <Target {...this.props}/>
            );
        }

        componentDidMount() {
            this.interval = setInterval(() => {
                this.color = this.color === initialColor ? 'red' : initialColor;
                findDOMNode(this).style.backgroundColor = this.color;
            }, 1000);
        }

        componentWillUnmount() {
            clearInterval(this.interval);
        }
    }
}
Как-то даже без рефа вышло.
> Как передать в компонент «модель»?

Вы у меня спрашиваете?

> В реальности этой «модели» вообще не будет — кнопка это props => ui, какая модель? Props — вот ваша модель, просто без стейта.

В реальности как раз такого обычно не бывает. Реальные компоненты содержат локальный стейт, который в общем часто довольно-таки нетривиально должен обрабатываться.

> вы что-то делаете не так.

Да нет, это значит, что задачи реальные, а не хелловорды. Вы же, наверное предложите либо повышать грануляцию компонент и прокидывать пропсы, либо использовать какой-то вариант с внешним стейт-менеджментом вроде редакса. Но когда у вас действительно серьезная логика, то в первом случае — получится просто туча пропсов, до уровня неюзабельности, а во втором — ну вы охренеете делать это через редакс, даже с сагами, поддерживать эту мешанину будет невозможно.

> Лихо вы байндингами прикрылись. То, что в реакте их нет, а в ангуляре есть, не делает из первого пхп, а из второго нет.

Это само по себе не делает, делают другие вещи (но то что реакт был написан для портирования сервер-сайд кода на пхп вообще факт известный, непонятно, чего вы с этим спорите), но вот о том, что ангуляр к пехепе отношения не имеет — как раз говорит :)

> Что, простите? Поясните

У меня есть функция f и фвп g, тогда я могу применить вторую к первой и сразу вызвать в коде своем: let x = g(f)(arg). Мне не требуется сперва класть перед этим новую ф-ю в отдельную переменную, вот так: let f2 = g(f), let x = f2(arg);
В jsx — приходится.

> Как-то даже без рефа вышло.

Отлично. И внезапно выясняется, что с functional component ваш HOC работать не будет. Таким образом, гипотеза о том, что все ваши красивые однострочники выше в реальности неприменимы — доказана. Ну и этот вариант явно проигрывает оригиналу на ангуляре. Причем проигрывает, как мне кажется, значительнее, чем проигрывал ангуляр на вашей задаче.
Вы у меня спрашиваете?
Не паясничайте.
В реальности как раз такого обычно не бывает. Реальные компоненты содержат локальный стейт, который в общем часто довольно-таки нетривиально должен обрабатываться.
В реальности как-раз стейт минимизируется. Либо изолируется, как я показал вам в примере с трюком. Который вы так элегантно проигнорировали.

Но когда у вас действительно серьезная логика, то в первом случае — получится просто туча пропсов, до уровня неюзабельности, а во втором — ну вы охренеете делать это через редакс, даже с сагами, поддерживать эту мешанину будет невозможно.
Давайте пример серьезной логики, сидящей в UI-слое! Вы же вроде за архитектуру? Что у вас серьезная логика в UI делает?

Да уж как-то прекрасно поддерживаю и не охреневаю. И не один проект. И даже через редакс, и даже с сагами, и прекрасно себя чувствую. Если у вас не получилось, не значит, что у остальных не получается.

У меня есть функция f и фвп g, тогда я могу применить вторую к первой и сразу вызвать в коде своем: let x = g(f)(arg). Мне не требуется сперва класть перед этим новую ф-ю в отдельную переменную, вот так: let f2 = g(f), let x = f2(arg);
В jsx — приходится.
А, так вот вы о чем. Тут соглашусь, но я не зря написал где-то выше по поводу сожалений о том, что взяли xml-подобный синтаксис, который сам по себе такие вещи не умеет. Как, собственно, любой движок с таким синтаксисом (взять тот же XAML). Ну и, к слову, вы в ангуляре вообще на лету ничего не можете сделать. JSX хотя бы через доп. декларацию позволяет такую динамику.

И внезапно выясняется, что с functional component ваш HOC работать не будет.
C чего вы взяли? Target прекрасно может быть SFC. Более того, я просто не включил в этот пример окружение из прошлого (без findDOMNode) — оно точно такое же. findDOMNode(this) получает дом-ноду на которой замаунчен текущий компонент, даже если он рендерит другой, дом-нода то одна на всех. В этом и прелесть (отсутствие обертки).
> Либо изолируется, как я показал вам в примере с трюком.

Этот трюк только на чистых ф-х работает, что будет, если надо заапдейтить стейт асинхронно?

> Что у вас серьезная логика в UI делает?

Управляет UI.
Представьте себе, не всем повезло работать с примитивными интерфейсами вроде твиттера :)
Ангуляр вообще больше предназначен для корпоративного сегмента.

> Ну и, к слову, вы в ангуляре вообще на лету ничего не можете сделать. JSX хотя бы через доп. декларацию позволяет такую динамику.

В Ангуляре я повешу атрибут на нужную ноду :)
Или сделаю через ng-content — туда можно совать что угодно, даже просто кусок дома.

> C чего вы взяли?

Да, действительно, this ссылается на полноценную компоненту. Но в любом случае, это не будет работать с ref, так что либо всей команде придется запретить использовать ref, либо функциональные директивы. Поболее предпочтительный вариант кажется очевидным.

> В этом и прелесть (отсутствие обертки).

Еще раз напоминаю, что вариант с наследованием в ангуляре тоже не дает обертки.
Этот трюк только на чистых ф-х работает, что будет, если надо заапдейтить стейт асинхронно?
Ну так в этом и смысл, что все операции над стейтом чистые и тестируемые. Всмысле асинхронно? Ну вызовите setState с этой функцией «асинхронно», в чем проблема?

Управляет UI.
Что ж она там так делает сосредоточенно? Ну пример давайте уже.

Представьте себе, не всем повезло работать с примитивными интерфейсами вроде твиттера :)
Я смотрю у вас там серьезно все, космолеты пилите, может поделитесь проектом?

Ангуляр вообще больше предназначен для корпоративного сегмента.
Это маркетинговый булшит, сверкающая сказка, заливаемая в уши менеджерам. Из-за этой легенды и появляются заказчики, слепо верящие в «корпоративность», «энтерпрайзность» и прочую лабуду. А разработка потом страдает, копаясь в этом недоделанном болоте, попутно вставляя костыли в поломанных после очередного обновления местах и штурмуя гитхаб в поисках тикета на баг. Корпоративный сегмент, бред какой.

Но в любом случае, это не будет работать с ref
И что? Оно и не должно

так что либо всей команде придется запретить использовать ref, либо функциональные директивы
Вы поймите, не работает не потому, что это бага или кривой дизайн, не работает потому, что у SFC нет инстанса — это функция props => ui. А если нет инстанса, нет и ссылки на него. Это же очевидно.

Поболее предпочтительный вариант кажется очевидным.
Именно — запретить использовать ref. Это противоречит декларативной идее. У нас например используется в крайне редких случаях, например в сервисных компонентах, снимающих размеры своей ноды после маунта. В остальном — почти всегда можно пойти декларативным путем.

Еще раз напоминаю, что вариант с наследованием в ангуляре тоже не дает обертки.
Еще раз напоминаю, что в этом варианте слишком много костылей.
> Ну вызовите setState с этой функцией «асинхронно», в чем проблема?

Что туда засунуть — промис? Так нельзя. Или заресолвить этот промис внутри компоненты и в then обновить стейт? Тогда у вас по сути часть логики подгрузки данных протекает в компоненту, о какой изоляции речь?

> Что ж она там так делает сосредоточенно? Ну пример давайте уже.

Собирает динамические многоэтажные формы, например.

> Это маркетинговый булшит, сверкающая сказка, заливаемая в уши менеджерам. Из-за этой легенды и появляются заказчики, слепо верящие в «корпоративность», «энтерпрайзность» и прочую лабуду.

Это ваше мнение, но факт остается фактом :)

> Именно — запретить использовать ref.

И использовать вместо него findDOMNode, который не рекомендован? Серьезно?

> В остальном — почти всегда можно пойти декларативным путем.

Ну вот задача — сделать blink-компоненту которую можно применить на любую другую (то есть без дополнительных пропсов). Она решается либо через реф (рекомендованный путь), либо через findDOMNode (не рекомендованный путь). Вы сейчас на полном серьезе предлагаете отказаться от рекомендованного варианта, ради чего? Чтобы сэкономить на спичках три строки при определении функциональных компонент?
Или заресолвить этот промис внутри компоненты и в then обновить стейт? Тогда у вас по сути часть логики подгрузки данных протекает в компоненту, о какой изоляции речь?
Простите, а что не так в setState из then? Вы хотите из ui-библиотеки сделать движок с автоматическим связыванием. Вот так нельзя. Стейт компонента нужен для синхронного изменения ui, все остальное вы можете использовать как угодно, через промисы, через стримы, через что угодно. Только если вам нужно изменить ui — вы это делаете через setState с нужными вам данными, например, в том же контейнере. Вы просто от реакта слишком многого хотите.

Собирает динамические многоэтажные формы, например.
Подозреваю, что там нет асинхронщины и нет стейта, а такая логика отлично выносится из компонента в модуль. Вообще, мне кажется, что такие вещи на реакте удобнее делать: form-configuration => ui — вот и весь компонент ConfigurableForm.

findDOMNode
Ну так там и сказано, что можно использовать, когда нужны всякие DOM measurements. Вы getBoundingClientRect без nativeElement в ангуляре тоже не вызовете. К слову, ref тоже не рекомендуется использовать.

Ну вот задача — сделать blink-компоненту которую можно применить на любую другую (то есть без дополнительных пропсов).
Я все-равно буду решать эту задачу через интерфейс. Еще и запрещу вызывать HOC на компонентах без backgroundColor пропса — это правильный декларативный путь, а патчить DOM-ноду чужого компонента — хак, нарушающий декларативность и абстракцию компонента.
type TBlinkingProps = {
  backgroundColor: string
};
type TTarget = React.ComponentClass<TBlinkingProps> | React.SFC<TBlinkingProps>;
const WithBlink = () => (Target: /* constraint! */ TTarget) => ...


Ну а если мне нужно сделать хак, так какая разница, что я это сделаю с помощью другого хака.
Druu Добавка:
Что туда засунуть — промис? Так нельзя. Или заресолвить этот промис внутри компоненты и в then обновить стейт? Тогда у вас по сути часть логики подгрузки данных протекает в компоненту, о какой изоляции речь?
Я вот прям вижу, что вам нужен mobx — достигается как раз то, что вам нужно, автоматическое связывание с моделью. Если все-таки mobx не по душе — заведите на модели стрим state$, а в контейнере подписка будет минимальной: this.props.model.state$.subscribe(state => this.setState(state))

И использовать вместо него findDOMNode, который не рекомендован? Серьезно?
Вы просто по рефу доступ к дом-ноде все-равно не получите. Все-равно придется вызывать findDOMNode.
> достигается как раз то, что вам нужно, автоматическое связывание с моделью.

Связывание с моделью — не проблема. Проблема в изоляции логики работы с моделью от вида.

> Если все-таки mobx не по душе — заведите на модели стрим state$, а в контейнере подписка будет минимальной:

И в чем разница? Кто и откуда этот стейт будет потом менять?

> Вы просто по рефу доступ к дом-ноде все-равно не получите.

Всмысле не получу?
reutn class extends React.Component {
  render() {
    return (
      <Component
        ref={(component) => { this.coponent = coponent; }} />
    );
  }
}

А к чему здесь будет доступ?
> Простите, а что не так в setState из then?

То, что я хочу, чтобы работа с там же сервером была вне вида, а не внутри него.

> Стейт компонента нужен для синхронного изменения ui

Тогда зачем он нужен вообще, если на практике все изменение стейта происходит асинхронно? Да что там — даже сама функция setState — асинхронная функция!

> Вы просто от реакта слишком многого хотите.

Так я и говорил, что реакт — это библиотека. Конечно же, никто в здравом уме не пользуется голым реактом. И после того, как вы реализуете полноценную архитектуру для работы, с разделением логики и представления, вы получите примерно ту же ситуацию, что и в ангуляре.

> Подозреваю, что там нет асинхронщины и нет стейта

Конечно же, есть, во время действий пользователя постоянно происходят запросы на сервер для подгрузки новых данных, и стейт есть, потому что во время заполнения данных надо решать какие дальше куски формы предоставлять для заполнения пользователю.

> Ну так там и сказано, что можно использовать, когда нужны всякие DOM measurements.

Так сказано, что надо использовать реф, прямо жирным выделено: «In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all. „

> Я все-равно буду решать эту задачу через интерфейс.

То есть, вы не можете ее решить. Потому что задача — уметь вызывать blink _на любом_ компоненте без изменений. Или вы теперь _на каждый_ компонент будете добавлять в пропсы color?

> а патчить DOM-ноду чужого компонента — хак, нарушающий декларативность и абстракцию компонента.

То есть задача не решшена по идеологическим причинам. Заметьте — в ангуляре, который вроде как фрейморк с жесткой архитектурой, эта архитектура решить задачу в стиле реакта никак не помешала.А вот в реакте — не поулчается. Потому я и сказал, что ангуляр более гибок. Он позволяет органично делать в рамках своей архитектуры то, что в реакте — грязный хак.

> Ну а если мне нужно сделать хак, так какая разница, что я это сделаю с помощью другого хака.

Ну не надо придуриваться.
Проблема в изоляции логики работы с моделью от вида.
И где проблема? Вы по-прежнему думаете, что стейт компонента — это модель, тогда как это не так. Когда перестанете, все станет легче и проще. Вы можете наворачивать тяжелейшую логику на любых технологиях, но не в UI-слое, за который отвечает реакт. Но вам придется самому написать связь между этой логикой и стейтом компонента.
Кто и откуда этот стейт будет потом менять?
Ну я не знаю, как вы проектируете приложения. Кому выделите полномочия на изменения этого стейта, тот и будет. React — ui-библиотека.
Всмысле не получу?
У меня для вас сюрприз, ref — это не dom-нода, а инстанс класса компонента Component, гляньте доки.
То, что я хочу, чтобы работа с там же сервером была вне вида, а не внутри него.
Я в недоумении. Вот вы знаете про экшены, редьюсеры и саги. А такое ощущения, что нет.
Тогда зачем он нужен вообще, если на практике все изменение стейта происходит асинхронно?
Вы возлагаете на этот стейт слишком много ответственности. Это не тот стейт, что ищите вы. Вы же помните, что вам дизайн реакта не позволяет (не будем сейчас про ref) как-то императивно работать с компонентами в дереве. Вам предоставлен единственный интерфейс для изменения состояния UI — промежуточный стейт.
Да что там — даже сама функция setState — асинхронная функция!
Это детали, и, если вы следуете дизайну, а не грубо нарушаете его, императивно работая с компонентами через ref, вам эта деталь вообще мешать не должна.
с разделением логики и представления, вы получите примерно ту же ситуацию, что и в ангуляре.
Ну я же не спорю об этом. Как воплощение идеи полноценного фреймворка, из коробки решающего все (почти) проблемы, меня он полностью устраивает. И я был бы счастлив видеть что-то подобное для реакта. Вот только реализация очень сильно подкачивает.
Конечно же, есть, во время действий пользователя постоянно происходят запросы на сервер для подгрузки новых данных, и стейт есть, потому что во время заполнения данных надо решать какие дальше куски формы предоставлять для заполнения пользователю.
Ну и отлично, держите весь этот стейт в вашей модели. Вот только вместо готовых байндингов у вас для связывания есть лишь один интерфейс — setState.
Так сказано, что надо использовать реф, прямо жирным выделено: «In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all. „
Ну так вы внимательно посмотрите, на каком компоненте ref возвращает DOM-ноду — на нативном. Для обычного класса ref вам вернет его инстанс, и вы сможете императивно дергать его методы. Но чтобы получить dom-ноду, вам все-равно придется вызвать findDOMNode.
То есть, вы не можете ее решить. Потому что задача — уметь вызывать blink _на любом_ компоненте без изменений. Или вы теперь _на каждый_ компонент будете добавлять в пропсы color?
Вы не заметили, что я ее решил? Почитайте примеры выше.
То есть задача не решшена по идеологическим причинам. Заметьте — в ангуляре, который вроде как фрейморк с жесткой архитектурой, эта архитектура решить задачу в стиле реакта никак не помешала.А вот в реакте — не поулчается. Потому я и сказал, что ангуляр более гибок. Он позволяет органично делать в рамках своей архитектуры то, что в реакте — грязный хак.
Перечитайте еще раз примеры выше. Где не решена? Доступ в DOM абсолютно такой же как в ангуляре. Только ангуляр застрял на уровне DOM, и это в порядке вещей нарушать абстракцию компонента, грязно патча его ноду, тогда как реакт оперирует более высокой абстракцией. Вы уж простите, но если в доках ангуляра не написано ничего про такие аннтипаттерны, это не значит, что это удачное решение. Это значит, что команда разработки просто закрыла глаза на явную архитектурную проблему. Тогда как в случае с реактом вам прямо в документации это описали. Не понимаю, почему вы возмущаетесь.
Ну не надо придуриваться.
Прошу вас, это вы не придуривайтесь.
> Вы можете наворачивать тяжелейшую логику на любых технологиях, но не в UI-слое, за который отвечает реакт.

Конечно, могу. При этом мне надо отделить эту логику от УИ-слоя. Сам реакт никаких средств для такого разделения не предоставляет (т.к. это не фреймворк, он и не должен их предоставлять), эти средства надо написать самому (или использовать сторонние дополнительные библиотеки). И после того, как вы их напишите (или подключите сторонние дополнительные библиотеки), вы возможность простой композиции компонент мгновенно утрачиваете, т.к. компонент без логики — бесполезный кусок.

> Кому выделите полномочия на изменения этого стейта, тот и будет. React — ui-библиотека.

Так я же об этом речь и веду. Как только вы добавите к реакту средства для работы с логикой, то вы и придете сразу к angular-style варианту в той или иной форме.

> У меня для вас сюрприз, ref — это не dom-нода, а инстанс класса компонента Component, гляньте доки.

И действительно. То есть, вам придется сделать _враппер_ из нативного html-элемента.

> Я в недоумении. Вот вы знаете про экшены, редьюсеры и саги. А такое ощущения, что нет.

Мы, вроде, ниже это обсуждали (о том, что класть локальный стейт в редакс хранилище — шизофрения)?

> Вы возлагаете на этот стейт слишком много ответственности. Это не тот стейт, что ищите вы.

Это локальный стейт компоненты.

> Вы же помните, что вам дизайн реакта не позволяет (не будем сейчас про ref) как-то императивно работать с компонентами в дереве.

Да все он позволяет. У вас единственная функция работы со стейтом (setState) — императивна, и стейт компонет — тоже императивен. Реакт — императивный фреймворк, а весь булшит про иммутабельность и функциональность — просто обычная маркетинговая ложь (или можете назвать более толерантным словом «лукавство», если «ложь» вам не нравится) фейсбука, чтобы продать товар. Как реакт может не позволять императивного взаимодействия с компонентами, если других вариантов он просто не предоставляет? Вопрос исключительно в том, как изолировать это взаимодействие — и вот средств изоляции искаробки нет, их надо реализовывать самому (или пользоваться сторонними).

> Вам предоставлен единственный интерфейс для изменения состояния UI — промежуточный стейт.

И если изолировать работу с этим промежуточном стейтом от вида — вы получаете решение типа ангуляра. Ну что тут непонятно? Я устал повторять одно и то же. Я же вам выше уже давным-давно рассказывал краткую историю о том, как мы переходим от голого вида реакта к ангуляровской архитектуре.

> Вот только реализация очень сильно подкачивает.

Реализации, которая бы удовлетворяла вашим хотелкам, не может существовать. Можете считать это математическим фактом. Либо вы мешаете модель с видом, тогда у вас одна сущность, которая комопозится, либо вы отделяете модель от вида — тогда вам надо отдельно их связывать и композить.

> Ну и отлично, держите весь этот стейт в вашей модели.

Прекрасно. Вот я вынес стейт в модель, теперь у меня есть два класса — модель и вид (React.Coponent). Эти два класса надо отдельно где-то привязать друг к другу (чтобы модель была именно стейтом данного вида), как вы, например, привязываете к компоненте стор редакса. Если вы сделаете HOC и примените ее к виду, то получите новый вид — который, опять же, надо будет привязать отдельно к вашей модели.

> Вы не заметили, что я ее решил?

Вы же вроде сами сказали, что это грязный хак. При этом вместо этого хака рекомендован к использованию другой хак (менее грязный).

Давайте в поведем итоги:
1. задача на реакте решаема
2. она решаема с грязным хаком (это реф, про FindDOMNode мы просто забываем, т.к. его не рекомендуется использовать, если задача может быть решена через ref, а на практике мы _будем_ выполнять рекомендации из доков)
3. для ее решения надо врапать компонент в нативный HTML-элемент (мне плевать, если честно, моя формулировка задачи это допускает. Это вы врапперы не любите :))
4. функциональные компоненты в любом случае могут не сработать (ни с рефом, ни с findDOMNode), по-этому в реальном проекте их использовать нельзя (исключение — локальные компоненты, которые не могут быть использованы «снаружи»)

> Только ангуляр застрял на уровне DOM, и это в порядке вещей нарушать абстракцию компонента, грязно патча его ноду, тогда как реакт оперирует более высокой абстракцией.

Вообще говоря, ангуляр ничего не знает о ДОМ, о ДОМ знает рендер. В компоненте вы вызываете функции рендера, а куда, что, и как он там будет рендерить — это дело десятое. Вы можете поменять рендер и он будет, например, строить нативный УИ вместо ДОМ браузера (и ваша ангуляровская компонента будет работать). Или виртуальный ДОМ (и потом скидывать с него новый стейт на реальный дом при помощи какого-то отдельного решения). Или генерировать прямо хтмл на сервере (как делает angular universal). То есть вы пишете
let nativeElement = this.element.nativeElement //нативный элемент, с ним мы не будем работать напрямую
let element = this.renderer.createElement(nativeElement) //абстрактный враппер
this.renderer.setElementStyle(element, "backgroundColor", "red") //вызов рендера для абстрактного элемента, что именно он сделает - зависит от рендера


Это пока вы работаете с ДОМ именно внутренними средствами ангуляра и не используете какие-то специфические АПИ, конечно же.

> Вы уж простите, но если в доках ангуляра не написано ничего про такие аннтипаттерны, это не значит, что это удачное решение.

Нет, это значит, что это вполне нормальное решение. Про антипаттерны в рамках анггуляра не особо пишут, потому что это «жесткий» фреймворк. То есть антипаттерны там реализовать просто _трудно_.Это будет требовать каких-то дополнительных, специфичных костылей и слома стандартного workflow. Условно, если что-то вам в ангуляре сделать просто (то есть оно хорошо вписывается в архитектуру фреймворка), то это почти наверняка не антипаттерн. В реакте же жесткой архитектуры нет, по-этому приходится пояснять «на словах», что можно, а чего — нельзя.
вы возможность простой композиции компонент мгновенно утрачиваете, т.к. компонент без логики — бесполезный кусок.
Каким образом вам эта логика мешает компоновке ui? это очень странно.
то вы и придете сразу к angular-style варианту в той или иной форме.
Я уже написал, что не имею претензий к идее, а только к реализации.
И действительно. То есть, вам придется сделать _враппер_ из нативного html-элемента.
Ну или просто findDOMNode(this). Ничего такого в этом нет, кроме предостережения в доках, потере абстракции и привязывания себя к DOM. Но если вам react-native не нужен, то этим можно пренебречь в узких случаях.
Мы, вроде, ниже это обсуждали (о том, что класть локальный стейт в редакс хранилище — шизофрения)?
То, что я хочу, чтобы работа с там же сервером была вне вида, а не внутри него.
Ну так вы же можете вызывать сервисы не в контейнере, а через «модель». Вам только нужно будет связать руками стейт модели со стейтом компонента.
Вопрос исключительно в том, как изолировать это взаимодействие — и вот средств изоляции искаробки нет, их надо реализовывать самому (или пользоваться сторонними)
Ну то есть вы все-таки согласны, что это достижимо?
И если изолировать работу с этим промежуточном стейтом от вида — вы получаете решение типа ангуляра. Ну что тут непонятно? Я устал повторять одно и то же. Я же вам выше уже давным-давно рассказывал краткую историю о том, как мы переходим от голого вида реакта к ангуляровской архитектуре.
Я уже выше написал. Меня не устраивает реализация.
Реализации, которая бы удовлетворяла вашим хотелкам, не может существовать.
Ну как-то так сложилось, что текущий стэк меня полностью (почти) устраивает. Не устраивают некоторые детали из серии react-router (хотя сознаюсь, четвертый я так и не попробовал пока).
Либо вы мешаете модель с видом, тогда у вас одна сущность
Либо я отделяю стейт компонента (ui) от стейта модели (логика). И вроде пазл складывается.
Прекрасно. Вот я вынес стейт в модель, теперь у меня есть два класса — модель и вид (React.Coponent). Эти два класса надо отдельно где-то привязать друг к другу (чтобы модель была именно стейтом данного вида), как вы, например, привязываете к компоненте стор редакса. Если вы сделаете HOC и примените ее к виду, то получите новый вид — который, опять же, надо будет привязать отдельно к вашей модели.
Не очень понимаю, что не так. Пути решения есть, механизм для связывания есть, ну да надо руками, ну так мы же помним про ui-библиотеку.
Давайте в поведем итоги:
Все сходится, я рад, что мы договорились.
Это пока вы работаете с ДОМ именно внутренними средствами ангуляра и не используете какие-то специфические АПИ, конечно же.
Это все красиво, но вы же понимаете, что это иллюзия :) Просто обертка вокруг обычного DOM-а.
В реакте же жесткой архитектуры нет, по-этому приходится пояснять «на словах», что можно, а чего — нельзя.
Я бы хотел (честно) увидеть какое-то решение, которое некоторые моменты бы запрещало. К сожалению, реакт действительно требует сильной дисциплины, так как очень легко оступиться.

Давайте зарезюмируем, мы вроде сошлись по всем пунктам: задачи решаются и там и там (почти) но с разной степенью костыльности, с разной степенью удобности, с разной степенью богатости инфраструктуры и с разной степенью требований к ментальному сдвигу. А то я просто уже начинаю терять нить спора. С другой стороны, хоть меня это обсуждение ангуляра уже порядком утомило, я с радостью еще поболтаю про реакт, и, может быть, смогу помочь решить какие-то (возможно) существующие проблемы. В противном случае, мы топчемся на месте :)
> Каким образом вам эта логика мешает компоновке ui?

Мне-то она не мешает, это вы были против того, что в ангуляровском варианте приходится совершать специальные телодвижения. Я просто считаю, что если вы допилите архитектурно в реакте разделение логики, то вы вынуждены будете совершать примерно те же телеодвижения, ±.

> Ну то есть вы все-таки согласны, что это достижимо?

Конечно. Иначе как бы на этом можно было что-то серьезное писать?

> Это все красиво, но вы же понимаете, что это иллюзия :) Просто обертка вокруг обычного DOM-а.

Я же вам специально привел в пример реально существующий universal, в котором рендер — уже нифига не обертка вокруг обычного ДОМа (которого на сервере нетути, и оборачивать нечего) :)
Вы если попробуете в universal отрендерить страницу с попыткой доступа к нативному элементу — вам закономерно выдаст ошибку, а если обернуть вызовы через рендер — все будет хорошо, то есть это не просто «обертка», транслирующая вызовы (она таковая только в случае браузера), это именно другой алгоритм рендера и с другим итоговым результатом (хтмл код вместо дома).

> Давайте зарезюмируем, мы вроде сошлись по всем пунктам: задачи решаются и там и там (почти) но с разной степенью костыльности

Я не вижу разной степени костыльности. Более того — если попарно решения сравнить, в обеих задачах, мне кажется, что реакт тут может и проиграть :)
Но это уже дело вкуса, так что можно и закончить, в любом случае, считаю, что дискуссия вышла достаточно продуктивной, спасибо :)
> Как-то даже без рефа вышло.

Странное достижение, учитывая что в доках как раз указывается предпочтительным реф :)
Вы думаете не понятно было, что вы меня поймать пытаетесь на том, что ref не работает на SFC? Ну так нет рефа — нет проблем. И все работает.
Слушайте, ну вы же не будете в реальности так делать. Вы будете использовать реф.
Эм, вообще-то я стараюсь минимизировать количество случаев, где мне этот реф вообще нужен. 99% случаев можно переписать декларативно.
Там же указано решение через инжект хоста (оно, кстати, и лучше, т.к. типы будут).
через HostListener:
@Component({
    selector: "test",
    template: `<div><button (click)='testClick($event)'></button></div>`
})
export class TestComponent {
    @Output() custom = new EventEmitter<string>();    

    testClick(e: any) {
        console.log("outer");
        this.custom.emit("to inner");        
    }
}

@Directive({
    selector: "[atest]",   
})
export class ATestDirective {
    @HostListener("custom", ["$event"]) acustom(e: string) {
        console.log("inner")
        console.log(e);
    };
}

// inner -> outer -> to inner
Там же указано решение через инжект хоста (оно, кстати, и лучше, т.к. типы будут).
Да, вот только мне явно нужно знать класс компонента, к которому я прицепляюсь.

через HostListener:
HostListener, как и HostBinding, работает только с нативными ивентами на дом-ноде хоста.
Да, вот только мне явно нужно знать класс компонента, к которому я прицепляюсь.

Так это ж хорошо. В чем недостаток то?

Иногда бывает нужно создать общую директиву для нескольких неопределенных классов. И положиться на рантайм проверки доступности нужных Input/Output. Тут идеально бы подошел интерфейс, но инжектор не умеет инжектить компоненты по интерфейсу. В общем случае же согласен, мне (эпопея с кнопкой) подходит.
Fesor Прошу прощения, не с кнопкой, а с контейнерами виджетов. С кнопкой вопрос открыт пока.
> ут идеально бы подошел интерфейс, но инжектор не умеет инжектить компоненты по интерфейсу.

Вообще умеет. Брать токен по типу — это лишь стандартное поведение, можно в аргументах в конструкторе сделать (@Inject(token) host: InterfaceComponent) и в хост заинжектится нужный токен, полностью будет как-то: ( Host() Inject(token) host: InterfaceComponent) (@Host() ограничивает инжектор, с-но, хостом), ну и в самой хост-компоненте надо зарегистрировать провайдер для соответствующего токена.
А можно поподробней? Вот например взять 1 интерфейс, 2 компонента с этим интерфейсом, и директиву, инжектящую нужный компонент (в рамках интерфейса) в зависимости от того, на каком тэге она висит?
@Component({
    selector: "component1",
    template: `<span>{{ data }}</span>`,
    providers: [{ provide: IComponentToken, useClass: forwardRef(() => Component1) }]

})
export class Component1 implements IComponent {
    @Input() data = "1";
}

@Component({
    selector: "component2",
    template: `<div>{{ data }}</div>`,
    providers: [{ provide: IComponentToken, useClass: forwardRef(() => Component1) }]
})
export class Component2 implements IComponent {
    @Input() data = "2";
}

@Directive({
    selector: "[mixin]",
})
export class MixinDirective implements OnInit  {
    constructor(@Host() @Inject(IComponentToken) private host: IComponent) {        
    }

    ngOnInit() {
        this.host.data = "3";
    }
}

както так
пропустил, вот этот кусок:
export interface IComponent {
    data: string;
}
export const IComponentToken = new InjectionToken<IComponent>("IComponent");

еще я мог в декларации провайдера напутать, я ее не помню, надо в доках смотреть
Ну и чего-то ничего.
<component1></component1>
<component2></component2>
<component1 mixin></component1>
<component2 mixin></component2>

На выходе:
1
2
1
2
Надо useExisting вместо useClass
Поменял — теперь «Error: No provider for Component1!»
Там опечатка, component1 вместо component2.
вот рабочий вариант, целиком:
export interface IComponent {
    data: string;
}
export const IComponentToken = new InjectionToken<IComponent>("IComponent");

@Component({
    selector: "component1",
    template: `<span>{{ data }}</span>`,
    providers: [{ provide: IComponentToken, useExisting: forwardRef(() => Component1) }]

})
export class Component1 implements IComponent {
    @Input() data = "1";
}

@Component({
    selector: "component2",
    template: `<div>{{ data }}</div>`,
    providers: [{ provide: IComponentToken, useExisting: forwardRef(() => Component2) }]
})
export class Component2 implements IComponent {
    @Input() data = "2";
}

@Directive({
    selector: "[mixin]",
})
export class MixinDirective implements OnInit  {
    constructor(@Host() @Inject(IComponentToken) private host: IComponent) {        
    }

    ngOnInit() {
        this.host.data = "3";
    }
}


> HostListener, как и HostBinding, работает только с нативными ивентами на дом-ноде хоста.

Нет, он со всеми работает, я специально проверил.

> Да, вот только мне явно нужно знать класс компонента, к которому я прицепляюсь.

Так вы уже определитесь — вам loaded не нужен (и тогда вы точно знаете класс) или нужен (тогда не знаете). Если класс неизвестен, то делается интерфейс, который компоненты, к которым можно подмешивать нужное поведение, реализуют, и хост инжектится потом по кастомному токену

> Еще больше не поверите, использую ngrx/store

Я тоже использую ngrx/store. При этом у меня есть обвязка из (только не пугайтесь)… кастомных декораторов, которая уменьшает типичный редаксовский бойлерплейт в разы ;)
    @Action()
    actionName(
        @Payload("argName") argName,
    ) {
        return { ...this.state, prop: argName };
    }

генерит сразу action-константу, action-creator, метод с вызовом dispatch'a и соответствующий кусок редьюсера. И, да, в отличии от декораторов класса, декораторы аргументов и методов, конечно, уже не просто функции.
Который раз одна и та же ерунда, сколько уже таких «упростителей redux» появилось, не счесть. Redux придумывали не для того, чтобы с ним боролись и пытались «вернуть все на свои места» запаковывая этот бойлерплейт обратно.
> Redux придумывали не для того, чтобы с ним боролись и пытались «вернуть все на свои места» запаковывая этот бойлерплейт обратно.

Так я этот бойлерплейт не запаковываю — он есть, я могу использовать любой его фрагмент. Я только не пишу его руками (но если будет надо в каком-то специфическом случае — могу и написать). Просто в оригинальном виде приложение на редаксе поддерживать невозможно в принципе. Собственно, сам Абрамов тоже пишет, что редакс — легковесная библиотека, которая предполагает использования разного рода обвязок для конкретного проекта. Примеры он на StackOverFlow приводил.
Redux придумывали не для того, чтобы с ним боролись и пытались «вернуть все на свои места» запаковывая этот бойлерплейт обратно.

Редакс придумывали идиоты, и раз уж приходится пользоваться этим говном, то стоит запаковывать его в какой-либо фантик) Бойлерпарт — это огромный источник квадратического повышения сложности приложения там, где повышение сложности должно быть линейным. А следовательно в каких-либо более-менее сложных программах он недопустим. Как и весь Редакс, впрочем. Но, так как вы сами меня сюда пригласили — вы уже об этом знаете))
Который раз одна и та же ерунда, сколько уже таких «упростителей redux» появилось, не счесть. Redux придумывали не для того, чтобы с ним боролись и пытались «вернуть все на свои места» запаковывая этот бойлерплейт обратно.

Позвольте! :) ducks + redux-act = счастье есть

Типизации пейлоада же там нет? Значит, счастье не достигнуто. Ну и проблема middleware остается.

Flow в помощь. Плюс FSA (но мне не нравится это ограничение, потому redux-act, а не redux-actions). Или я не понял вопроса?


И позвольте узнать, какая проблема у middleware?

> И позвольте узнать, какая проблема у middleware?

Оно в сущности своей кривое и костыльное. Вообще, изначально проблема в том, что и простые экшоны и асинхронные являются экшонами. Сам факт существования сущности «асинхронный экшон».

> Flow в помощь.

Ну так для этого код должен быть такой, чтобы его можно было без болей типизировать. У вас результат createAction принимает data какого-то произвольного типа, как сделать, чтобы он был конкретного типа (пейлоад вашего экшона), и чтобы в редьюсере потом при определении соответствующей функции этот тип автоматически использовался? Я не вижу способа. Это и есть основная проблема дизайна редакса (ну, кроме упомянутой выше с асинхронными экшонами). Есть экшон криейтор, есть редьюсер, они имеют единую зависимость (чисто по смыслу) — это экшон, но его по факту в коде по дефолту нет. В итоге эту проблему надо решать — либо отдельно выписывать экшон и на него явно ссылаться (вариант дефолтного редакса), либо делать какюу-то обвязку, которая прибьет редьюсер к экшону (как у меня выше).
Оно в сущности своей кривое и костыльное.
Поясните
Сам факт существования сущности «асинхронный экшон».
Если вы следуете CQRS+ES (подобию для redux), то «асинхнронных экшенов» у вас не будет. Но будут асинхронные процессы обработки синхронных экшенов, например саги.
как сделать, чтобы он был конкретного типа (пейлоад вашего экшона), и чтобы в редьюсере потом при определении соответствующей функции этот тип автоматически использовался?
Свести типы экшенов (FSA) в один discriminated union с общим ключом type. В редьюсере switch/case сможет выполнить сужение типа в зависимости от type в кейсе. (Это я про TS, если что)
> Свести типы экшенов (FSA) в один discriminated union с общим ключом type. В редьюсере switch/case сможет выполнить сужение типа в зависимости от type в кейсе.

Ну так вы тогда возвращаетесь к классическому редаксу, то есть ни createAction (), ни createReducer из примера по вашей ссылке использовать нельзя (createReducer ведь идет _вместо_ switch/case).

> Но будут асинхронные процессы обработки синхронных экшенов, например саги.

Саги еще имеют более-мене человеческое лицо, но ситуация принципиалньо не меняется — у вас в редьюсер может прилететь обычный экшон, с которым потом выполнится чистая функция по изменению стора, а может — непонятная асинк-хрень, которая грязно спавнит другие асинк-хрени. В итоге тот факт что редьюсер — чистый, вам ничего не дает, ведь логика мидлеваре входит в логику редьюсера де-факто. Это перекладывание из левого кармана в правый. Решается все на самом деле просто — достаточно разрешить спавнить экшоны из редьюсеров (тогда экшоны остаются экшонами (событиями), а редьюсеры — редьюсерами (реакциями)), но это нарушает религиозные убеждения автора библиотеки.
Ну так вы тогда возвращаетесь к классическому редаксу,
ну я же не с проста пишу про «упростителей redux»

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

ведь логика мидлеваре входит в логику редьюсера де-факто.
Это с какой стати. То, что все упорно не хотят отделять DATA_FETCH_REQUESTED от DATA_FETCH_SUCCEDED и продолжают, продолжают пихать это в thunk'и, в промисы, в любую схожую чушь — не значит, что это верно. Зачем вообще redux для этого? Скомбинированные асинхронные вызовы прекрасно складываются в контейнеры, причем даже нормализовать эти процессы не нужно.
достаточно разрешить спавнить экшоны из редьюсеров (тогда экшоны остаются экшонами (событиями), а редьюсеры — редьюсерами (реакциями)), но это нарушает религиозные убеждения автора библиотеки
Зачем вам вообще этот подход? Вы пробовали mobx?
> Это с какой стати.

С простой стати. С точки зрения клиента, когда я задиспатчил экшон, то он ушел в редьюсер. Все, что происходит после диспатча экшона — происходит в редьюсере, о мидлеваре клиент не знает. Так что мидлеваре — это часть редьюсера де-факто. Просто вынесенная в отдельный кусок, так, что у нас есть «типа чистая» и «типа грязная» части. С чистой частью вот только все нормально, т.к. чистые функции хорошо композятся, а вот с грязной — выходит беда бедовая.

> DATA_FETCH_REQUESTED от DATA_FETCH_SUCCEDED и продолжают

Асинхронные реквесты вообще в редакс пихать не надо, только если уж совсем приперло (надо кеширование, разделять контент между многими компонентами, а пропсы прокидывать лень и т.п.). Их надо оставлять в локальном стейте компонент. То же, как оно сейчас многие делают (хреначить по десяток экшенов на каждый реквест, а их в приложении сотни… эту кашу поддерживать не проще чем программу на брейнфаке) — откровенная шизофрения.

> Зачем вам вообще этот подход?

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

> Вы пробовали mobx?

Ой, фу.
С точки зрения клиента, когда я задиспатчил экшон, то он ушел в редьюсер
С точки зрения клиента, вы вообще не должны ничего знать о том, куда ушел экшен, как он обрабатывается, где он обрабатывается, в редьюсере иди в мидлваре. Вообще ничего. Вы через dispatch сообщаете, что произшло событие — все. Нет «типа чистой» части и «типа грязной». Есть текущее статичное состояние приложения, являющееся результатом композиции редьюсеров. И есть процессы, которые могут инициировать транзишн этого состояния, обрабатываемый теме же редьюсерами так же статично. Ну т.е. если у вас есть автомат LOGGED_OUT -> LOGGING_IN -> LOGGED_IN -> LOGGING_OUT -> LOGGED_OUT, вы каждое из этих состояний описываете статично в редьюсере в виде какой-то комбинации флагов/переменных, реагируя на соответстующие экшены. Если у вас есть состояние «ошибка», привязанное к какому-либо из предыдущих состояний, вы так же под него отводите отдельное состояние и описываете его в редьюсере. Если вам не нужна такая подробность — вам не нужен redux.

Асинхронные реквесты вообще в редакс пихать не надо
Асинхронные реквесты общего вида (тот же процесс авторизации) прекрасно уживаются в сагах. А вот реквесты по, например, id (ну, допустим, данные для виджета по его id) — уже плохо. И вот их как раз удобно складывать в контейнер виджета, потому как на приложение в целом они не влияют.

стараюсь ничего не совать без крайней необходимости (в противовес идиотичному «перенесем все в редакс»)
Полностью поддерживаю, «перенесем все в редакс» уже не раз обсуждалось
> Нет «типа чистой» части и «типа грязной».

Ну нет, они по факту есть. Мы пишем код, обрабатывающий экщон, и этот код вполне осознанно автором редакса делится на две части — одна в редьюсерах, и другая в асинхронных экшонах, исполняемых через мидлеваре. Если разрешить из редьюсера бросать экшоны (в чем проблемы в общем-то никакой и нет по факту, это не меняет control-flow), то тогда асинхронные экшоны сведутся к обычным функциям а мидлеваре можно к хренам убрать. То есть в общем система сильно упрощается, и при этом ничем особо мы не жертвуем.

> Асинхронные реквесты общего вида (тот же процесс авторизации) прекрасно уживаются в сагах.

А результат авторизации как раз обычно должен быть доступен всему (почти всему) приложению, глобально, так что это, можно сказать, канонический пример, когда редакс оправдан.

> И вот их как раз удобно складывать в контейнер виджета, потому как на приложение в целом они не влияют.

Ну я об этом и говорил. Есть состояние, которое по смыслу _должно_ быть глобальным (те же данные залогиненого юзера) — и его менеджим редаксом. А то, что по смыслу локально и не требуется по каким-либо причинам класть в стор — пусть оно и будет локальным.
в асинхронных экшонах, исполняемых через мидлеваре
Экшены, а точнее события, по природе своей не могут быть асинхронными. Что такой «аснинхронный экшен» LOGIN_REQUESTED? А то, что они крутятся в мидлваре — это детали реализации.
Давайте я не буду вам цитировать дословно документацию и рассказывать почему редьюсеры должны быть чистыми и без сайд-эффектов в виде вызовов апи и выбросов других экшенов. Это как-то неприлично.
Ведь если вам корневая идея redux не подходит, есть обычный flux вместе с waitFor внутри диспетчера. А то получается, вы хаете redux из-за отсутствия возможности сделать то, ради чего он не предназначен.

Ну хоть по остальным пунктам сошлись.
Druu
Кстати, раз уж вам так хочется в редьюсерах менеджить эффекты — вот ближайший кандидат. Порт Elm, все эффекты декларативны, синтаксис оставляет желать лучшего.
> Synchronous state transitions caused by returning a new state from the reducer in response to an action are just one of all possible effects an action can have on application state.

АХахахахаха, :cry: :lol:
Ну я прям реально под стол сполз, и ребятам для того, чтобы прийти к этой очевидной идее надо было:

> used and followed the progression of Redux and the Elm Architecture, and after trying other effect patterns for Redux

Натуральная комедия. Ну и решение конечно такое же смешное — совать промис в стор. В чем проблема просто его дернуть? Нет, тогда ж мы пуристость потеряем, по-этому вместо этого мы засунем промис в стор, а потом его уже дернут :D
В чем проблема просто его дернуть?

Проблема в тестируемости и потере детерминированности. Засунете вы в стор не промис, а декларативное описание этого промиса. Вспомните саги — ровно то же.
> Проблема в тестируемости и потере детерминированности. Засунете вы в стор не промис, а декларативное описание этого промиса.

Ну да, промис же автоматом стартует при создании, так что описание промиса а не промис. Ну это не так важно, речь о том, что разницы-то никакой нет, тогда зачем огород городить? Ну стартанет твой промис потом, сразу после выполнения редьюсера. Ну и что? что это дает? Нам единственное что нужно — это чтобы следующий экшон не был обработан редьюсером до того, как будет обработан предыдущий, чтобы поток выполнения был линеаризован. И все.

> Вспомните саги — ровно то же.

Нет, сага — это свободная монада, интерпретатор.

Ну да, промис же автоматом стартуем при создании. Речь о том, что разницы-то никакой нет, тогда зачем огород городить?
Разница есть — ваш редьюсер недетерминирован. В разные моменты времени вы получите разные выходные результаты от одинаковых входных параметров.

Нет, сага — это свободная монада, интерпретатор.
Имелся ввиду подход саг к декларативным эффектам. Вы же не резолвите промисы руками в них?
> Разница есть — ваш редьюсер недетерминирован.

Вполне детерменирован — он же вернет в точности тот же результат. Просто по пути заспавнит экшон, который придет потом в стор. В точности то же самое и в приведенном выше решении — просто экшен заспавнится несколько раньше. Единственное, что нам нужно — чтобы сама последовательность экшенов не нарушалась — ведь состояние стора больше ни от чего не зависит! Если последовательность обработки экшенов неизменна — то и состояние стора на каждом шаге будет тем же. А когда и как запускаются промисы — вобщем-то не важно, это деталь реализации, которая не влияет на результат.
он же вернет в точности тот же результат.
Нет, в зависимости от состояния этого промиса, результаты будут разные.
Просто по пути заспавнит экшон, который придет потом в стор.
В том то и дело, что редьюсер идеологически — (state, action) => state — чистый, синхронный и детерменированный. Это не Абрамов ненавистный придумал — это придумали давным-давно в далекой-далекой галактике функциональных языках. И все возможные проблемы с сайд-эффектами там тоже давным-давно уже решены. Он не должен что-то там спавнить по ходу, так как сразу обрастет сайд-эффектом. Вы можете упаковать свой эффект в декларативную обертку, чтобы сохранить свойства редьюсера. И я не очень понимаю, почему выполнение эффекта вне редьюсера вызывает столько негодования. Держа редьюсеры чистыми, вы на халяву получаете простоту отладки, простоту тестирования, тайм-тревелинг, undo/redo и прочее-прочее.
> Нет, в зависимости от состояния этого промиса, результаты будут разные.

С чего бы это? Мы же не делаем await промиса в редьюсере, редьюсер только создает промис, но не ожидает его завершения. Потом в then промиса диспатчится какой-то экшон и уходит в стор. В итоге у нас в стор в обоих случаях приходит вполне детерменированно два одинаковых экшона в одном и том же порядке.

> Он не должен что-то там спавнить по ходу, так как сразу обрастет сайд-эффектом.

Этот сайдэффект все равно есть. И от того что вы его отодвинули ничего не меняется — ну это просто заметание под ковер.

> И я не очень понимаю, почему выполнение эффекта вне редьюсера вызывает столько негодования.

Потому что это неудобно. Вот и все. И при этом ничего не дает.

> Держа редьюсеры чистыми, вы на халяву получаете простоту отладки, простоту тестирования, тайм-тревелинг, undo/redo и прочее-прочее.

В описываемом мной случае все точно так же — у нас линейная последовательность экшенов, которую можно отматывать и так далее.
С чего бы это?
Идеологически на уровне редьюсера стейт с одним и тем же промисом (даже ссылка та же) но в разных состояних — это 3 разных стейта, так как по сути описывают 3 разных состояния: pending, success, fail. Это же разные состояния? Разные. Проблема в том, что вы прячете это состояние внутри одного и того же промиса, и стейт редьюсера у вас становится один, хотя на самом деле, там появился эффект. Тестировать такое добро крайне сложно. Кроме того, вы теряете контроль над ситуацией в случае например протухания сессии. Например, вы делаете запрос, а в then у вас редирект куда-нибудь. А тут бац, сессия сдохла — как это в редьюсере похендлить? Хендлить внутри какого-нибудь сервиса, который вы вызываете из редьюсера? Так себе, в каждом сервисе хендлить? А если нужно по протуханию сессии выкинуть экшен редиректа на логин-страницу? Из сервиса его выбрасывать?
Я понимаю, что описываю вам юзкейс саги, и хочу на этом заострить внимание. Если все эффекты обрабатываются на другом уровне, нежели редьюсеры, вы можете адекватно выстроить любой асинхронный флоу, а редьюсеры просто будут отражать статичное состояние приложения.

Этот сайдэффект все равно есть. И от того что вы его отодвинули ничего не меняется — ну это просто заметание под ковер.
Меняется, выше ответил. Плюс тестировать это просто феерически проще — deepequal по этому контейнеру с эффектом, это ж просто объект.

В описываемом мной случае все точно так же — у нас линейная последовательность экшенов, которую можно отматывать и так далее.
Как вы синхронно отмотаете список экшенов, когда среди них есть асинхронные? То что из store.dispatch торчит наружу промис — это костыль. Идеологически это ж неправильно.

Или вот есть у вас оплог этих экшенов, надо его намотать на стор. А там какая-то асинхронщина, которая другие экшены выбрасывает. А в оплоге они уже есть, упс.

Вот если бы у вас стейт был сохранен целиком, без скрытых в промисах подсостояний, тогда все было бы просто. Собственно undo/redo так и делается. Там не экшены применяются/отменяются, а просто заменяется весь стейт.
> Идеологически на уровне редьюсера стейт с одним и тем же промисом (даже ссылка та же) но в разных состояних — это 3 разных стейта, так как по сути описывают 3 разных состояния: pending, success, fail. Это же разные состояния? Разные.

Это состояние промиса. Зачем его тащить в стейт? ПРусть будет локлаьным для промиса.

> А тут бац, сессия сдохла — как это в редьюсере похендлить?

А как вы это хендлите вне редьюсера?

> Как вы синхронно отмотаете список экшенов, когда среди них есть асинхронные?

Среди них нет асинхронных. Любой экшн — это просто тип с пейлоадом.

> А там какая-то асинхронщина, которая другие экшены выбрасывает. А в оплоге они уже есть, упс.

Вот именно. Это проблема, когда у нас произвольные сайд-эффекты, но когда эффекты ограничены (например — исключительно спавном, возможно асинхронным, других экшенов), то они не оказывают влияния ни на стор, ни на ход рендера, по-этому мы можем просто мокнуть ф-ю, которая внутри редьюсера вызывает эффект, в x => {return;}, которая ничего не делает.

> Собственно undo/redo так и делается. Там не экшены применяются/отменяются, а просто заменяется весь стейт.

В этом случае редьюсеры не вызываются и вообще никаких проблем нет. Мотай себе стейт куда захочешь и как захочешь.
Это состояние промиса. Зачем его тащить в стейт? Пусть будет локлаьным для промиса.
Подождите, как вы вообще из редьюсера собрались диспатчить экшены? В смысле, чем? Максимум, что я могу представить, это сохранить промис в стейт редьюсера, и потом где-то к нему прицепиться. Таким образом, он все-таки попадает в стейт, а значит все его состояние автоматически становится состоянием редьюсера.

А как вы это хендлите вне редьюсера?
Через саги, все сервисы (если их так можно назвать) — там. А из саг есть доступ к put, call, race и т.д. Сервис для апи, просто в случае протухания сессии просто вызывает put с соответствующим экшеном. Сага авторизации слушает этот экшен через take и выплевывает через put экшен редиректа на логин. Так же как она это делает в случае проверки авторизации на старте, в случае успешного логина и т.п. Все декларативно и прекрасно тестируется.

Среди них нет асинхронных. Любой экшн — это просто тип с пейлоадом.
Да, я что-то не про то, вы ж про асинхронщину в редьюсере. Собственно и оплог тут не при чем тогда.
> Давайте я не буду вам цитировать дословно документацию и рассказывать почему редьюсеры должны быть чистыми и без сайд-эффектов в виде вызовов апи и выбросов других экшенов.

Не надо, я ее читал. Ничего кроме религии там по этому поводу нет. Штука вся в том, что _не имеет значения_ спавните вы экшон через мидлеваре или через редьюсер — это приводит к совершенно одинаковому control-flow вашего приложения со всеми вытекающими.
Очевидно, редьюсеры вы не тестируете. Иначе быстро бы завыли от разбора эффектов в тестах.
«Чистую» часть редьюсеров вы можете тестировать так же просто. а «Грязная»… ну она и в оригинальном редаксе была грязной, просто находилась не в редьюсерах.
Ну так а с декларативными эффектами я и грязную так же просто тестирую. И совсем без каких-либо моков.
> Ну так а с декларативными эффектами я и грязную так же просто тестирую.

С декларативными эффектами она не грязная :)
Но оверхед от достижения этой декларативности кажется значительно превышающим какой-то один мок.
Увы, в 99% далекооо не один. Один сервис подключает другой, тот третий, тот четвертый. В итоге мокается все окружение. Когда проще генераторов с call из эффектов саг набросать. Вспомнить хотя бы тестирование ангуляра — бррр.
Дело в том, что реакт — сам по себе и так реактивно апдейтится при изменении состояния. MobX не меняет чего-то кардинально, а просто добавляет некоторые возможности работы со стейтом к родным — но не расширяя, а заменяя существующую инфраструктуру. При этом MobX — очень тонкая библиотека, и сама по себе все еще неюзабельна (ее надо тоже как и сам реакт допиливать до архитектурно полноценного решения). Ну и сам по себе подобный реактивный подход ограничен — он удобен в задачах, которые хорошо на него ложатся, а если мы выходим за зону комфорта — начинаются лютейшие костыли.
Тут товарищ TheShock любит напоминать про Стокгольмский синдром.

Ну, позволю себе предположить вывод, что в JS каждая либа — немного насильник. Жаль, не знаком с Ангуларом достаточно хорошо — не могу подключиться к аргументированному спору.

И на каждом компоненте ее вызывать вместе с обычными наследованием, чтобы унаследовать еще какой-то кусок?

Ну, для справедливости, в Реакте тоже такое используется — например, декоратор для проброски контекста везде, где необходимо. Это даже удобнее других альтернатив, которые я знаю)

Я уже молчу про совершенно дикие mapStateToProps и connect, которые те же декораторы. но используются повсеменстно как ядро архитектуры, а не как исключение.

Но пока то, что я вижу с Ангуларом — визуально очень ужасно в сравнении с очень изящным Реактом. Может, если пописать лично, то впечатление будет иное?
> Но пока то, что я вижу с Ангуларом — визуально очень ужасно в сравнении с очень изящным Реактом. Может, если пописать лично, то впечатление будет иное?

Не думаю, обычно если что-то с виду не нравится — то оно не нравится. У меня вот как кровь текла из глаз от jsx'a, так и продолжает течь, и ничего не сделаешь.

Ну, для справедливости, в Реакте тоже такое используется — например, декоратор для проброски контекста везде, где необходимо. Это даже удобнее других альтернатив, которые я знаю)
Ну я ж не об этом, а о том, что в силу чудовищного дизайна приходится подпирать костылями банальные вещи. Контекст в реакте — это совсем другой уровень абстракции, нежели наследование классов.

Может, если пописать лично, то впечатление будет иное?
Попробовать точно стоит, хоть мне он и насильно прилетел (желание заказщика). Но будте готовы к минам на каждом шагу, так как фреймворк еще очень сырой и какие-то вещи фиксятся прямо сейчас.
OCaml — прекрасная штука, особенно в виде bucklescript. Но, к сожалению, в прод это все добро не взять. Фейсбук, видимо, может себе позволить поиск разрабов на окамле за бешеные деньги или, что более вероятно, учить своих. Но для подавляющего большинства это неподъемно. Вот и остается лишь облизываться и завидовать.

Я вот засматривался все на purescript, а сейчас влюбился (просто втрескался) в idris. Но я знаю, что с началом новой недели вернется унылый es6 и чуть менее унылый ts.
>а сейчас влюбился (просто втрескался) в idris

В js нормально компиляет? Можете примеров показать? (можно в личку) Тоже присматриваюсь, думаю, стоит не стоит. У меня есть пачка собственных проектов, где никто над душой не стоит, тоже пока пилю на ts, но хотеть хорошо жить не запретишь…
Я пока лишь на старте и вживую ничего серьезного не собирал в js, но кодогенерация есть, причем таргетов достаточно много. В однострочный хелловорлд на выходе пакуется рантайм, хоть и небольшой.
Но вообще, как язык, крайне рекомендую, dependent types — это именно то, чего не хватает. А вот очень хорошая книжка.
вернется унылый es6

А я уже выработал привычку его любить. :) Функциональные фишки, Flow, Standard. Открыл для себя VSCode — это щастье.

Это да, но когда знаешь, что в природе существуют нормальные алгебраические типы данных и нормальный паттерн-матчинг, хочется рыдать, когда пишешь в 5 раз больше кода, чтобы реализовать тоже самое на ts через discriminated unions и switch/case по ним.
А es6 уныл, потому-что там даже этого нет :)
MirageOS — такое чувство пока докер развиваеться, тут стоит funeral.
Ещё одно. Знаете, никогда не любил CoffeeScript. (Я знаю, что внезапный переход). Однако там была одна замечательная штука. Она называется Элвис-оператор. Когда вам нужно лезть во вложенный объект, вы могли вместо точки поставить знак вопрос и точка, и сказать что если вот эта штука null или undefined, то вернуть undefined
var friend = props.?user.?friends[0].?friend;

Кстати, на обычном JS аналогичная запись может выглядеть так:
var friend = (((props ||{}).user ||{}).friends[0] ||{}).friend;
Конечно не так красиво, но вполне читабельно.
А объектов-то сколько создается — красота.
А если серьезно, такие вещи (наличие или отсутствие вложенных объектов) проще и удобней решать через статическую типизацию. Другое дело, что в тип нельзя запихнуть проверку на количество элементов в массиве. Но я выше написал про idris. Да, экзотика заморская, но там так можно.

А какая разница сколько объектов? Они все gc по быстрому соберутся.
Статическая типизация не решит проблему, когда вы получаете данные из, например, ивентов.

gc это совсем не по-быстрому. gc это по закон подлости: непонятно когда и в самый неподходящий момент.

С такими данными не может быть неподходящего момента) Это же нигде не используемые объекты, которые после проверки даже не оставляют ссылок.

gc по быстрому соберутся
я бы не стал так полагаться на GC, особенно в JS. Никогда не знаешь, чем он занимается в данный момент и не лень ли ему будет освободить эти объекты. Опять же, если эти проверки происходят в циклах или каком-нибудь mousemove, вас неприятно удивит таймлайн работы gc.

когда вы получаете данные из, например, ивентов.
Ивенты/IO — да, хоть я и не припомню в ивентах таких вложенных структур.
В общем случае, по коду лучше не таскать эту ерунду из проверок. Даже если они присутствует в том или ином виде, лучше по возможности минимизировать их количество и сложить где-нибудь совсем сбоку. Если это xhr, то можно прикрыться каким-нибудь tcomb.

Какой дурак будет такое в циклах использовать? Хотя да… Бывают.

Зачем юзать либы, их же придумали чтобы гитхаб забивать.


var f = _.get(props, 'user.friends.0.friend')

Все хорошо, но строка вместо человеческого объекта — плохо. IDE не понимает такой нотации, рефакторинг усложнен.


Лучше уж в ООП стиле —


public user: User;
// .. something ..
User friend = user.getFriend(0);

А там внутри все нужные проверки и возврат. И хорошо бы какого-нибудь аналога optional, а не null и не undefined.

>но строка вместо человеческого объекта — плохо. IDE не понимает такой нотации, рефакторинг усложнен.

прокси в этом вопросе хорошо помогают:

export const Path = function<T>(constr: T, path?: string): T{

    const target: string[] = typeof path === 'undefined' ? [] : (Array.isArray(path) ? path : [path]);

    return new Proxy(target, {
      get(target: any, prop: any) {
        return typeof constr === 'object'
            && prop in (constr as any)
                ? Path((constr as any)[prop], target.concat([prop]))
                : target[prop];
      },
      set(target: any, prop: string, value: any) {
        return false;
      }
    }) as T;


вызываем:

const path = Path(объект_на_котором_надо_строить_путь);

используем:

какаято_функция(path.путь.до.нужного.свойства)

какаято_функция получит массив токенов — [«путь», «до», «нужного», «свойства»]
IDE счастлива, статичная типизация работает, если надо — в рантайме обращение к несуществующему свойству можно ханлить, кодер сыт и доволен )

Хоть ниже меня и минуснули за подобное но ссылки все таки дам — github и npm
Лучше уж в ООП стиле —

в этом нет смысла. А null-coalescing operator вроде как уже в stage-1 ES.

UFO just landed and posted this here

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


var friend = null;
props && props.user && props.user.friends && props.user.friends[0] && /* !!! */ friend = props.user.friends[0].friend;

Жутко конечно, но работает достаточно хорошо и без багов.

а я задолбался этим страдать и запилил простой чекер, похвастаюсь пользуясь случаем — github и npm. Доку правда обновить надо, там кое что добавилось, но в целом по исходникам понятно — там по сути многоэтажный однострочник. strongly typed module, тесты, все дела :)

То ничего, в friend как было так и останется null. Так что всё нормально)

Пример с чистым js не совсем корректен, так как не хватает ещё одной проверки на существования массива прежде чем получить из него элемент[0]. А во вторых правильный код вообще не должен вызываться при таком условии, то есть реальной необходимости в этом коде может за всю карьеру и не случится. А то что Вы считаете вполне читаемым, так это код слабого джуниора.

Мои слова относятся к коду `var friend = (((props ||{}).user ||{}).friends[0] ||{}).friend;`, я ошибся когда написал в Вашем блоке.
А то что Вы считаете вполне читаемым, так это код слабого джуниора.
Это вы от ангуляра такой злой

Приведёте пример правильного и читаемого кода?
let { firstName, lastName, frends } = props;

// та-да-м, ооп!

frends.getUserByIndex( 0 );

Если Вы спросите «а где проверка на не существование frends », то ответ банален, уровень на котором я обращаюсь к frends просто не должен знать о том что ему не нужно знать, Вы же программист, так сделайте так чтобы ему всегда было хорошо.
UFO just landed and posted this here
  1. среди всего выделяются парочка фреймворков с уклоном в модульность/компоненты
  2. проходит хайп.
  3. все потихоньку пилят на этих фреймворках.
UFO just landed and posted this here
пока меня не задели за живое. tc-39/proposal-cancelable-promises — мы все очень ждем в стандарте языка, который был написан человеком из Google, который шёл-шёл-шёл, дошёл до Stage-1 (или Stage-2, я не помню), и в один прекрасный момент был отозван
Это говорит о том, что человеки разумные могут не только совершать ошибки, но и признавать их, даже когда все зашло очень далеко.

Cancelable Promises изначально появились из-за попытки решить проблему не на том уровне абстракции. Промис по смыслу является значением. Как должна выглядеть отмена значения, чисто с позиции логики? Проблемы в логике не решаются кодингом и костылями. Bluebird это уже два раза доказал на практике, сначала сделав одну реализацию C-P с кучей «особенностей», а потом еще раз переписав с нуля уже с другой кучей.

Как вариант, нормальный способ отмены чего-либо предложен в Observable, которые представляют собой последовательности. Promise и Observable это две штуки, которые прекрасно решают свои задачи и уживаются вместе.
нормальный способ отмены чего-либо предложен в Observable

А где про это можно почитать?

Красиво это делается через takeUntil. Вот пример, он на ng2, но суть не меняется.
Согласен, отменять промисы это звучит так себе, но fetch нельзя принудительно отменить и это делает на половину бесполезным
Сам промис подразумевает, что действие стартует непосредственно в момент обещания. Они не ленивые. Создание промиса сродни запуску ракеты, которая должна доставить спутник на орбиту — когда она отрывается от земли уже поздно передумывать. С промисами в этом плане есть только два варианта: либо не обещать совсем, либо пытаться игнорировать результат как будто ни чего не было.

Получается, что fetch должен выставлять наружу не промис, а что-то еще, что позволит достичь нужного эффекта. Вариант с Observable рассматривали выше. Можно еще попытаться приспособить yield. В принципе это те же яйца, наизнанку. С помощью генератора можно мониторить статус, а при необходимости забросить внутрь исключение, которое приведет к остановке.
Если вы не в курсе, работу Эвана Ю сейчас финансирует более, чем на 60% такая маленькая китайская компания под названием Alibaba.

И все же его Эван полностью сам поднял и развивает, большой респект ему =) (сам vue мне не нравится и не рекомендую его)

Чем vue не нравится? Что предпочитаете вместо?

Позвольте, я тоже отвечу на этот вопрос. Vue пугает тем, что это монолит. Полновесный фреймворк, который даёт тебе ответы на 95% вопросов. Многие считают это благом. Но у меня уже был печальный опыт с Метеором. Хочется больше контроля над составными частями проекта. Потом, как только я узнал о Reactive Native, а было это осенью 2015-ого года, то заплакал — прочувствовал избавление от мучений, проект на Cordova под iOS тормозил нещадно. Инвестиции в освоение React-а — лишь промежуточный этап на пути к React Native. Угу, под Vue есть Weex для мобильной разработки. Но он пока очень скромно выглядит в сравнении с React Native. Expo.io — отдельный праздник. Дальше-больше, сравните выбор компонентов для Weex и RN. Ещё, React+MobX — это и есть Vue, как написано в документации Vue, только у меня больше пространства для маневров. К примеру, активность коммитов в репозитории MobX за последний месяц подозрительно упала до ноля. Полез смотреть, чем занят автор, а он уже два месяца активно пилит MobX-State-Tree — и это будет революция, надеюсь. Ещё в огород Vue, не понимаю я радости от модификаторов в шаблонах — вместо JS предлагается очередной птичий язык внутри шаблонизатора — проходили, я просто очень давно живу. :)

Фреймворки, библиотекифреймоворки.
Чем крупнее монстр, тем больше хайпа с их инструментов.
Тем больше привлечённых раков, на которых потом ориентируются раки следующего поколения.
И так, вырастает целая империя псевдопроектировщиков, которые агрессивно настроены против всяких «устаревших» по их мнению инструментов типа jQuery.

Даже не стесняются заниматься подменой понятий, типа React — библиотека js (ведь так заявил фейсбук!), а не фреймворк. Которая работает за счёт интерпретатора и внутренней абстракции. Но нет, попереть против гигантов — кишка тонка.

Страдает, конечно же, от этого всего работодатель, ведь он должен обеспечивать весь этот коллектив гениев.

Да здравствует JS Hype Empire!

P.S. Речь идёт о крупных проектах, а не лендинге.
P.S. Есть опыт работы на js фреймворках и вся критика — исключительно личное мнение. Просто наболело.
С одной стороны дикая инфляция технологий доставляет неудобства: сложно ощущать себя профессионалом и продаваться наравне с профи в других областях. С другой, обеспечивает работодателю стабильный поток заказов с поддержки и дешманский труд. Особенно когда речь идет о крупных проектах, а не лендинге. Это IT, детка. Бизнес простых чисел. Ты либо единица, либо ноль. :)
А потом нас, раков, зовут в крупный проект на котором все SPA выполнено на jQuery, сдобрено костылями и велосипедами, без какой либо вменяемой документации и мануала. Этот проект не автоматизирован, часто не покрыт тестами, такие простые вещи как html5 history натягиваются на него через боль и страдания, а уже готовый функционал работает через раз и никогда не будет пофикшен потому что единственный кто знает как это работает либо в отпуске, либо болеет, либо занят важными фичами. Мы видим весь этот ужас и бежим как можно быстрее от людей, которые готовы тратить столько своего времени на поддержку этого монстра и упорно отказываются шагать в ногу со временем.
А работодатель рвет волосы на голове потому что не может найти специалистов, готовых поддерживать этот треш и угар за вменяемые деньги и в случае ухода ведущего разработчика он будет вынужден потерять много времени на поиск его замены и, скорее всего, переписать все на более актуальные технологии.

Rect это библиотека не потому что так говорит Facebook, а потому что отвечает лишь за представление данных, для всего остального используются другие библиотеки (или фреймворки).

В общем <очень_толсто>.jpg

PS:
Я не могу назвать себя гуру, но для того что бы понять как работать с react мне понадобилось 2 дня, для того что бы въехать в Redux еще один день чтение доки и общение в тематических чатах. Что бы поднять проект на Angular 2, разобраться в TS и научиться писать тесты мне понадобилось около 2х недель.
Если вы считаете что это слишком много для изучения, то задумайтесь о выборе профессии. Может быть стоит уйти из программирования?
для того что бы понять как работать с react мне понадобилось 2 дня, для того что бы въехать в Redux еще один день
Что бы поднять проект на Angular 2, разобраться в TS и научиться писать тесты мне понадобилось около 2х недель
На личном опыте убедился, что это совсем не две недели, так как требовались более изощренные решения, чем те, что покрываются офф-доками. (можете почитать эпический срач в меру аргументированные обсуждения чуть выше)
У меня все было наоборот, неделю я поднимал React + MobX. И в итоге под каждый чих приходилось либо писать копомненты / устанавливать библиотеки.
Минуту я поднимал Angular (слава angular-cli). И с удовольствием теперь пользуюсь модулями из-под коробки.

Попробуйте Create React App и Create React App Native.

Их в то время еще не было, а проблемы скудности функционала из коробки они не решают вроде как.
Для себя я решил так:
В разработке продукта участвует много человек и выделено много времени — React;
Разработку ведут 1-2 человека: Angular т/к помогает сэкономить много человеко-часов.

Скажите, почему вы предпочитаете flow?

Прочитайте это — тоже Илья Климов научил.

Как же хорошо читать это, забросив разработку на JS еще в 2014, и понимать, что сделал всё правильно.
Sign up to leave a comment.

Articles