> Проблема в тестируемости и потере детерминированности. Засунете вы в стор не промис, а декларативное описание этого промиса.
Ну да, промис же автоматом стартует при создании, так что описание промиса а не промис. Ну это не так важно, речь о том, что разницы-то никакой нет, тогда зачем огород городить? Ну стартанет твой промис потом, сразу после выполнения редьюсера. Ну и что? что это дает? Нам единственное что нужно — это чтобы следующий экшон не был обработан редьюсером до того, как будет обработан предыдущий, чтобы поток выполнения был линеаризован. И все.
Если он за ui не отвечает, то он и разметки содержать не должен, а значит — это attribute directive, а не component:
> Attribute directives—change the appearance or _behavior_ of an element, component, or another directive.
> 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
> Давайте я не буду вам цитировать дословно документацию и рассказывать почему редьюсеры должны быть чистыми и без сайд-эффектов в виде вызовов апи и выбросов других экшенов.
Не надо, я ее читал. Ничего кроме религии там по этому поводу нет. Штука вся в том, что _не имеет значения_ спавните вы экшон через мидлеваре или через редьюсер — это приводит к совершенно одинаковому control-flow вашего приложения со всеми вытекающими.
> Прекрасно — декларативно добавьте свою обертку в разметку. Это же не костыль.
Это мне для каждой директивы обертку добавлять? :D
;)
> Судя по офф-докам, так делать не стоит. Кроме того, использовать компонент по аттрибуту на уже существующем компоненте ангуляр не дает из-за конфликта имен.
А все просто — это используется в тех случаях, когда нам _обязательно_ надо избавиться от враппера, по-этому рекомендации из офф-доков не работают. И в этих случаях нам надо раскрывать компоненту в существующий тег — а значит он не будет другой компонентой :)
> Но остается последний вариант — когда нужно расширить кастомный компонент без обертки.
Если у нас кастомный компонент, то тогда нет проблем его обертывать.
Ну нет, они по факту есть. Мы пишем код, обрабатывающий экщон, и этот код вполне осознанно автором редакса делится на две части — одна в редьюсерах, и другая в асинхронных экшонах, исполняемых через мидлеваре. Если разрешить из редьюсера бросать экшоны (в чем проблемы в общем-то никакой и нет по факту, это не меняет control-flow), то тогда асинхронные экшоны сведутся к обычным функциям а мидлеваре можно к хренам убрать. То есть в общем система сильно упрощается, и при этом ничем особо мы не жертвуем.
> Асинхронные реквесты общего вида (тот же процесс авторизации) прекрасно уживаются в сагах.
А результат авторизации как раз обычно должен быть доступен всему (почти всему) приложению, глобально, так что это, можно сказать, канонический пример, когда редакс оправдан.
> И вот их как раз удобно складывать в контейнер виджета, потому как на приложение в целом они не влияют.
Ну я об этом и говорил. Есть состояние, которое по смыслу _должно_ быть глобальным (те же данные залогиненого юзера) — и его менеджим редаксом. А то, что по смыслу локально и не требуется по каким-либо причинам класть в стор — пусть оно и будет локальным.
> Но если я в реакте «наследую» (расширяю через враппер со спредом, но без лишней дом-ноды) «вид» (компонент), то я получу новый компонент. Со старой моделью.
Ну кто вас какой гибкости лишает? Вы точно так же можете создать новый вид и связать его с расширением старой модели (с добавленным property «icon»), именно это и происходит в моем примере выше. Просто в реакте вы не делаете второй шаг (не описываете связывание), так как модель не выделена, связывать нечего, не с чем, а потому и не нужно. В реакте у вас есть возможность определить компоненту, как единую сущность. В ангуляре — нет. В ангуляре всегда есть разделение на вид и модель и всегда нужно описать их связь. И вы _можете_ сделать так, чтобы эта связь формировалась определенным образом автоматически (@ComponentInherit ваш), но это не является дефолтным поведением.
> Проблему я назвал — мне нужно расширить кнопку (окей, компонент строки таблицы) без доп. обертки
Ну а если я скажу, что мне надо тоже сделать кнопку, но с оберткой? Будут костыли уже в реакте. Я согласен, что иногда (весьма редко) действительно надо без оберток. Тогда компонент применяется атрибут-селектором и все, так что проблема полностью надуманна.
С простой стати. С точки зрения клиента, когда я задиспатчил экшон, то он ушел в редьюсер. Все, что происходит после диспатча экшона — происходит в редьюсере, о мидлеваре клиент не знает. Так что мидлеваре — это часть редьюсера де-факто. Просто вынесенная в отдельный кусок, так, что у нас есть «типа чистая» и «типа грязная» части. С чистой частью вот только все нормально, т.к. чистые функции хорошо композятся, а вот с грязной — выходит беда бедовая.
> DATA_FETCH_REQUESTED от DATA_FETCH_SUCCEDED и продолжают
Асинхронные реквесты вообще в редакс пихать не надо, только если уж совсем приперло (надо кеширование, разделять контент между многими компонентами, а пропсы прокидывать лень и т.п.). Их надо оставлять в локальном стейте компонент. То же, как оно сейчас многие делают (хреначить по десяток экшенов на каждый реквест, а их в приложении сотни… эту кашу поддерживать не проще чем программу на брейнфаке) — откровенная шизофрения.
> Зачем вам вообще этот подход?
Так если доработать напильником и не упарываться религией автора — то вполне удобно. Ну и к слову, поскольку глобальное состояние — это само по себе очень серьезный антипаттерн, я туда просто стараюсь ничего не совать без крайней необходимости (в противовес идиотичному «перенесем все в редакс»).
> Вы считаете целесообразным лепить InheritMetadata в каждом компоненте, чтобы помочь наследованию сделать дело?
В реакте все постоянно так делают и никаких проблем это не вызывает. Это во-первых. Во-вторых — метаданные из Component наследоваться и не должны, так что нет, _я_ не предлагаю. Это _вы_ предлагаете такое поведение. Я вам лишь указал, как вы можете его достичь — это делается абсолютно бесплатно, переопределением декоратора.
> Что мешает template, style и компанию складывать в class properties?
Ничего не мешает. Но это не будет работать.
> Не наследуются все метаданные.
А зависимости, видать, инжектятся в потомках за счет святого духа? А инпуты /оутпуты базового класса откуда берутся? Или вы не в курсе, что это все — тоже метаданные?
> Ну то есть про дизайн и проблемы вы согласны?
Вы же пока не назвали проблем.
> Святая корова, да почему??
Потому что поведение будет неопределено. И определить его каким-либо осмысленным образом нельзя. Вы как-то пропустили мой основной тезис из предыдущего поста. Вид и модель — это две разные сущности. Наследуя класс компонента — вы наследуете модель. А вид — он отдельно, он вообще не класс, даже.
Вот у вас в реакте модели как бы нет, есть только вид (и потому он же — модель). Вы пишите свой компонент, пишите — потом обнаруживаете, что в нем слишком много логики. Вы выносите эту логику со всей работой с состоянием в отдельный класс — теперь у вас есть модель и есть вид, при этом модель ничего не знает о виде, и если вы отнаследуете модель — то не получите нового компонента. Точно так же, вы не получаете нового компонента при наследовании модели в ангуляре. И _не должны_.
Далее вы хотите сделать все более молодежно и модно. По SOLID. У вас ведь вид привязан к модели — а это нехорошо. Вы их развязываете — теперь у вас вид зависит не от модели, а от ее интерфейса, а конкретная модель инжектится. А какая модель? Неизвестно — теперь вид ничего не знает о конкретной модели. Значит, нужна конфигурация, и в ней у вас будет написано что-то вроде: «модель Х рендерится видом Y». И если вы хотите отнаследовать одну компоненту от другой — то это сразу не заработает, вам надо пойти и добавить соответствующее вхождение в конфиг, чтобы связать вид с моделью. Как в ангуляре.
Потом проходит некоторое время и оказывается, что ваша конфигурация превратилась в огромную портянку. Да и вообще — прыгать туда-сюда, чтобы узнать что там к чему инжектится и с чем связано — не совсем удобно. Вы распиливаете вашу конфигурацию на куски — по куску на модель — и каждый кусок, соответствующий конфигурации модели, храните вместе с моделью. А конфигуратор потом собирает из этих кусков исходный полный конфиг. И у вас получается архитектура ангуляра. В ангуляре все вышеобозначенные шаги сделаны — потому что это фреймворк. У него есть архитектура. Реакт — это библиотека, в ней архитектуры нет. Предполагается, что ее надо создавать самому для конкретного приложения. И когда вы ее создадите, то точно так же у вас вид будет отделен от модели и будет отдельно наследоваться…
> Свести типы экшенов (FSA) в один discriminated union с общим ключом type. В редьюсере switch/case сможет выполнить сужение типа в зависимости от type в кейсе.
Ну так вы тогда возвращаетесь к классическому редаксу, то есть ни createAction (), ни createReducer из примера по вашей ссылке использовать нельзя (createReducer ведь идет _вместо_ switch/case).
> Но будут асинхронные процессы обработки синхронных экшенов, например саги.
Саги еще имеют более-мене человеческое лицо, но ситуация принципиалньо не меняется — у вас в редьюсер может прилететь обычный экшон, с которым потом выполнится чистая функция по изменению стора, а может — непонятная асинк-хрень, которая грязно спавнит другие асинк-хрени. В итоге тот факт что редьюсер — чистый, вам ничего не дает, ведь логика мидлеваре входит в логику редьюсера де-факто. Это перекладывание из левого кармана в правый. Решается все на самом деле просто — достаточно разрешить спавнить экшоны из редьюсеров (тогда экшоны остаются экшонами (событиями), а редьюсеры — редьюсерами (реакциями)), но это нарушает религиозные убеждения автора библиотеки.
> И позвольте узнать, какая проблема у middleware?
Оно в сущности своей кривое и костыльное. Вообще, изначально проблема в том, что и простые экшоны и асинхронные являются экшонами. Сам факт существования сущности «асинхронный экшон».
> Flow в помощь.
Ну так для этого код должен быть такой, чтобы его можно было без болей типизировать. У вас результат createAction принимает data какого-то произвольного типа, как сделать, чтобы он был конкретного типа (пейлоад вашего экшона), и чтобы в редьюсере потом при определении соответствующей функции этот тип автоматически использовался? Я не вижу способа. Это и есть основная проблема дизайна редакса (ну, кроме упомянутой выше с асинхронными экшонами). Есть экшон криейтор, есть редьюсер, они имеют единую зависимость (чисто по смыслу) — это экшон, но его по факту в коде по дефолту нет. В итоге эту проблему надо решать — либо отдельно выписывать экшон и на него явно ссылаться (вариант дефолтного редакса), либо делать какюу-то обвязку, которая прибьет редьюсер к экшону (как у меня выше).
> Вы в упор меня не слышите, и складывается впечатление, что специально. Целесообразно использовать инструмент для решения задачи, но нецелесообразно лепить костыли для решения проблемы в дизайне.
То что мы обсуждаем — и есть обычное использование инструмента для решения задачи. Мне непонятно, почему вы готовы писать обычные функции в реакте, но против того, чтобы делать то же самое в ангуляре в точно том же самом контексте.
> Вот что мешало включить всю конфигурацию декоратора в тело класса?
То, что это просто нельзя сделать сколько-нибудь осмысленным образом. Те метаданные, что имеет смысл наследовать, наследуются. А те, что наследовать нельзя — не наследуются. Ни стили, ни темплейты, ни другие подобные вещи наследовать просто нельзя, это чревато серьезными проблемами. То есть, эту фичу невозможно сделать так, чтобы она не была заведомо поломанной.
> Мнимая «переиспользуемость» классов отдельно от этих декораторов?
Вот, кстати, да. Если желаете — можете вынести конфигурацию декоратора и переиспользовать ее в разных классах (наследниках, например). И никаких «кастомных декораторов».
> Не притворяйтесь, вы прекрасно поняли задачу.
Нет, не понял. Ваш пример на реакте был уже позже, я когда его увидел — то переделал.
> Мой посыл в том, что дизайн кривой, пользоваться неудобно, проблемы есть.
Удобство — это субъективная категория. Я не вижу какой-либо разницы по сравнению с реактом. Мне, например, jsx не удобен и кривое разделение ответственности (ну тут, собственно, проблема в том, что реакт — не фреймворк, а библиотека, и для нормальной работы так же как и редакс предполагается дорабатывать его напильником, так что все так и должно быть).
> Нет, не сродни.
Это ваше мнение.
> Мы говорим о наследовании, а не о композиции. Если ButtonWithIcon наследует Button, то подразумевается что все поведение, вплоть до работы с EventEmitter наследуется, а не копипастится.
Во-первых, оно и наследуется. Не наследуется шаблон, потому что его нельзя осмысленно наследовать, а ивентэмиттеры и т.п. — наследуются, я же об этом выше уже говорил. Во-вторых, нет, это именно композиция. Вы пишите одну компоненту, в которой вызывается другая компонента, естественно вы передаете во вторую компоненты требуемые ею аргументы, точно так же как вызывая функцию из другой функции. Это происходит на уровне controller/view (шаблона). А на уровне модели (ComponentClass) вы наследуете. В реакте модель гвоздями прибита к вью (их можно разделить, вообще, но сам реакт по дефолту не о том), в ангуляре — это две разные и независимые сущности. По-этому наследуются метаданные которые относятся к модели (соответствующие декораторам членов), но не наследуются те, что относятся к виду Это же простая и ясная структура. В чем проблема?
> Что дизайн оправдан? Нет.
Это ваше мнение. Единственную вашу объективную претензию мы рассмотрели, если других таких же нету — я не вижу смысла продолжать. Потому что «здесь вызов функции — использование инструмента, а там — костыль» или «оно врапает а я хочу чтобы не врапало» — это не разговор.
> Но пока то, что я вижу с Ангуларом — визуально очень ужасно в сравнении с очень изящным Реактом. Может, если пописать лично, то впечатление будет иное?
Не думаю, обычно если что-то с виду не нравится — то оно не нравится. У меня вот как кровь текла из глаз от jsx'a, так и продолжает течь, и ничего не сделаешь.
> Это и выяснять не надо было. Разговор был о целесообразности, а не о классификации.
Ну вы согласны тогда, что писать свои метаредьюсеры нецелесообразно? И hoc в реакте — тоже? Если да, то тогда ваша позиция понятна. Если нет — то тогда тут наблюдается явная непоследовательность.
> По кругу мы ходим из-за проблем ненаследования метаданных, а не наследования inputs/outputs.
Тогда к чему вы приплели inputs/outputs?
> вам нужно пойти во все наследники и руками пробросить это все в шаблоне (как в случае с пробросом clicked).
Это сродни претензии к тому, что если вы в функцию добавили аргумент, то надо везде, где эта функция вызывается, добавить этот аргумент. Я тут ничего плохого не вижу.
> Кроме того, если у кнопки есть стили, а в 99% случаев они есть, то они не наследуются.
Так это правильно. Они легко могут сломаться, если их отнаследовать.
> Совсем не то же самое. Иконка должна быть внутри my-button.
Так бы сразу и сказали, я же не могу мысли читать:
> Redux придумывали не для того, чтобы с ним боролись и пытались «вернуть все на свои места» запаковывая этот бойлерплейт обратно.
Так я этот бойлерплейт не запаковываю — он есть, я могу использовать любой его фрагмент. Я только не пишу его руками (но если будет надо в каком-то специфическом случае — могу и написать). Просто в оригинальном виде приложение на редаксе поддерживать невозможно в принципе. Собственно, сам Абрамов тоже пишет, что редакс — легковесная библиотека, которая предполагает использования разного рода обвязок для конкретного проекта. Примеры он на StackOverFlow приводил.
Я тоже использую ngrx/store. При этом у меня есть обвязка из (только не пугайтесь)… кастомных декораторов, которая уменьшает типичный редаксовский бойлерплейт в разы ;)
генерит сразу action-константу, action-creator, метод с вызовом dispatch'a и соответствующий кусок редьюсера. И, да, в отличии от декораторов класса, декораторы аргументов и методов, конечно, уже не просто функции.
Когда вы на реакте применяете какую-то ф-ю к компоненту — вы фактически и используете декоратор :)
//так вы пишите:
const EnhancedComponent = higherOrderComponent(WrappedComponent);
//а можно записать с синтаксисом декоратора:
@higherOrderComponent
export class WrappedComponent {
}
то есть «определить и использовать в реакте HOC» и «определить и использовать кастомный декоратор» — это _одно и то же_. HOC из реакта — это _и есть_ декораторы классов.
> что стандартный Component использовать нельзя, нужно использовать кастомный. И мы возвращаемся к огромному бессмысленному мануалу.
Команда же может запомнить, что надо юзать higherOrderComponent выше? :)
> Окей, тут соглашусь
Значит, по факту декларативности мы вопрос утрясли.
Ну да, промис же автоматом стартует при создании, так что описание промиса а не промис. Ну это не так важно, речь о том, что разницы-то никакой нет, тогда зачем огород городить? Ну стартанет твой промис потом, сразу после выполнения редьюсера. Ну и что? что это дает? Нам единственное что нужно — это чтобы следующий экшон не был обработан редьюсером до того, как будет обработан предыдущий, чтобы поток выполнения был линеаризован. И все.
> Вспомните саги — ровно то же.
Нет, сага — это свободная монада, интерпретатор.
Если он за ui не отвечает, то он и разметки содержать не должен, а значит — это attribute directive, а не component:
> Attribute directives—change the appearance or _behavior_ of an element, component, or another directive.
Это я намекая на ваши претензии вида: «это мне на каждую компоненту @Inheritance ставить? оО».
> Увы, есть.
Не совсем понял ваше объяснение. :host селектор проблему не решает?
АХахахахаха, :cry: :lol:
Ну я прям реально под стол сполз, и ребятам для того, чтобы прийти к этой очевидной идее надо было:
> used and followed the progression of Redux and the Elm Architecture, and after trying other effect patterns for Redux
Натуральная комедия. Ну и решение конечно такое же смешное — совать промис в стор. В чем проблема просто его дернуть? Нет, тогда ж мы пуристость потеряем, по-этому вместо этого мы засунем промис в стор, а потом его уже дернут :D
Не надо, я ее читал. Ничего кроме религии там по этому поводу нет. Штука вся в том, что _не имеет значения_ спавните вы экшон через мидлеваре или через редьюсер — это приводит к совершенно одинаковому control-flow вашего приложения со всеми вытекающими.
Это мне для каждой директивы обертку добавлять? :D
;)
> Судя по офф-докам, так делать не стоит. Кроме того, использовать компонент по аттрибуту на уже существующем компоненте ангуляр не дает из-за конфликта имен.
А все просто — это используется в тех случаях, когда нам _обязательно_ надо избавиться от враппера, по-этому рекомендации из офф-доков не работают. И в этих случаях нам надо раскрывать компоненту в существующий тег — а значит он не будет другой компонентой :)
> Но остается последний вариант — когда нужно расширить кастомный компонент без обертки.
Если у нас кастомный компонент, то тогда нет проблем его обертывать.
Ну нет, они по факту есть. Мы пишем код, обрабатывающий экщон, и этот код вполне осознанно автором редакса делится на две части — одна в редьюсерах, и другая в асинхронных экшонах, исполняемых через мидлеваре. Если разрешить из редьюсера бросать экшоны (в чем проблемы в общем-то никакой и нет по факту, это не меняет control-flow), то тогда асинхронные экшоны сведутся к обычным функциям а мидлеваре можно к хренам убрать. То есть в общем система сильно упрощается, и при этом ничем особо мы не жертвуем.
> Асинхронные реквесты общего вида (тот же процесс авторизации) прекрасно уживаются в сагах.
А результат авторизации как раз обычно должен быть доступен всему (почти всему) приложению, глобально, так что это, можно сказать, канонический пример, когда редакс оправдан.
> И вот их как раз удобно складывать в контейнер виджета, потому как на приложение в целом они не влияют.
Ну я об этом и говорил. Есть состояние, которое по смыслу _должно_ быть глобальным (те же данные залогиненого юзера) — и его менеджим редаксом. А то, что по смыслу локально и не требуется по каким-либо причинам класть в стор — пусть оно и будет локальным.
Ну кто вас какой гибкости лишает? Вы точно так же можете создать новый вид и связать его с расширением старой модели (с добавленным property «icon»), именно это и происходит в моем примере выше. Просто в реакте вы не делаете второй шаг (не описываете связывание), так как модель не выделена, связывать нечего, не с чем, а потому и не нужно. В реакте у вас есть возможность определить компоненту, как единую сущность. В ангуляре — нет. В ангуляре всегда есть разделение на вид и модель и всегда нужно описать их связь. И вы _можете_ сделать так, чтобы эта связь формировалась определенным образом автоматически (@ComponentInherit ваш), но это не является дефолтным поведением.
> Проблему я назвал — мне нужно расширить кнопку (окей, компонент строки таблицы) без доп. обертки
Ну а если я скажу, что мне надо тоже сделать кнопку, но с оберткой? Будут костыли уже в реакте. Я согласен, что иногда (весьма редко) действительно надо без оберток. Тогда компонент применяется атрибут-селектором и все, так что проблема полностью надуманна.
С простой стати. С точки зрения клиента, когда я задиспатчил экшон, то он ушел в редьюсер. Все, что происходит после диспатча экшона — происходит в редьюсере, о мидлеваре клиент не знает. Так что мидлеваре — это часть редьюсера де-факто. Просто вынесенная в отдельный кусок, так, что у нас есть «типа чистая» и «типа грязная» части. С чистой частью вот только все нормально, т.к. чистые функции хорошо композятся, а вот с грязной — выходит беда бедовая.
> DATA_FETCH_REQUESTED от DATA_FETCH_SUCCEDED и продолжают
Асинхронные реквесты вообще в редакс пихать не надо, только если уж совсем приперло (надо кеширование, разделять контент между многими компонентами, а пропсы прокидывать лень и т.п.). Их надо оставлять в локальном стейте компонент. То же, как оно сейчас многие делают (хреначить по десяток экшенов на каждый реквест, а их в приложении сотни… эту кашу поддерживать не проще чем программу на брейнфаке) — откровенная шизофрения.
> Зачем вам вообще этот подход?
Так если доработать напильником и не упарываться религией автора — то вполне удобно. Ну и к слову, поскольку глобальное состояние — это само по себе очень серьезный антипаттерн, я туда просто стараюсь ничего не совать без крайней необходимости (в противовес идиотичному «перенесем все в редакс»).
> Вы пробовали mobx?
Ой, фу.
В реакте все постоянно так делают и никаких проблем это не вызывает. Это во-первых. Во-вторых — метаданные из Component наследоваться и не должны, так что нет, _я_ не предлагаю. Это _вы_ предлагаете такое поведение. Я вам лишь указал, как вы можете его достичь — это делается абсолютно бесплатно, переопределением декоратора.
> Что мешает template, style и компанию складывать в class properties?
Ничего не мешает. Но это не будет работать.
> Не наследуются все метаданные.
А зависимости, видать, инжектятся в потомках за счет святого духа? А инпуты /оутпуты базового класса откуда берутся? Или вы не в курсе, что это все — тоже метаданные?
> Ну то есть про дизайн и проблемы вы согласны?
Вы же пока не назвали проблем.
> Святая корова, да почему??
Потому что поведение будет неопределено. И определить его каким-либо осмысленным образом нельзя. Вы как-то пропустили мой основной тезис из предыдущего поста. Вид и модель — это две разные сущности. Наследуя класс компонента — вы наследуете модель. А вид — он отдельно, он вообще не класс, даже.
Вот у вас в реакте модели как бы нет, есть только вид (и потому он же — модель). Вы пишите свой компонент, пишите — потом обнаруживаете, что в нем слишком много логики. Вы выносите эту логику со всей работой с состоянием в отдельный класс — теперь у вас есть модель и есть вид, при этом модель ничего не знает о виде, и если вы отнаследуете модель — то не получите нового компонента. Точно так же, вы не получаете нового компонента при наследовании модели в ангуляре. И _не должны_.
Далее вы хотите сделать все более молодежно и модно. По SOLID. У вас ведь вид привязан к модели — а это нехорошо. Вы их развязываете — теперь у вас вид зависит не от модели, а от ее интерфейса, а конкретная модель инжектится. А какая модель? Неизвестно — теперь вид ничего не знает о конкретной модели. Значит, нужна конфигурация, и в ней у вас будет написано что-то вроде: «модель Х рендерится видом Y». И если вы хотите отнаследовать одну компоненту от другой — то это сразу не заработает, вам надо пойти и добавить соответствующее вхождение в конфиг, чтобы связать вид с моделью. Как в ангуляре.
Потом проходит некоторое время и оказывается, что ваша конфигурация превратилась в огромную портянку. Да и вообще — прыгать туда-сюда, чтобы узнать что там к чему инжектится и с чем связано — не совсем удобно. Вы распиливаете вашу конфигурацию на куски — по куску на модель — и каждый кусок, соответствующий конфигурации модели, храните вместе с моделью. А конфигуратор потом собирает из этих кусков исходный полный конфиг. И у вас получается архитектура ангуляра. В ангуляре все вышеобозначенные шаги сделаны — потому что это фреймворк. У него есть архитектура. Реакт — это библиотека, в ней архитектуры нет. Предполагается, что ее надо создавать самому для конкретного приложения. И когда вы ее создадите, то точно так же у вас вид будет отделен от модели и будет отдельно наследоваться…
Ну так вы тогда возвращаетесь к классическому редаксу, то есть ни createAction (), ни createReducer из примера по вашей ссылке использовать нельзя (createReducer ведь идет _вместо_ switch/case).
> Но будут асинхронные процессы обработки синхронных экшенов, например саги.
Саги еще имеют более-мене человеческое лицо, но ситуация принципиалньо не меняется — у вас в редьюсер может прилететь обычный экшон, с которым потом выполнится чистая функция по изменению стора, а может — непонятная асинк-хрень, которая грязно спавнит другие асинк-хрени. В итоге тот факт что редьюсер — чистый, вам ничего не дает, ведь логика мидлеваре входит в логику редьюсера де-факто. Это перекладывание из левого кармана в правый. Решается все на самом деле просто — достаточно разрешить спавнить экшоны из редьюсеров (тогда экшоны остаются экшонами (событиями), а редьюсеры — редьюсерами (реакциями)), но это нарушает религиозные убеждения автора библиотеки.
Оно в сущности своей кривое и костыльное. Вообще, изначально проблема в том, что и простые экшоны и асинхронные являются экшонами. Сам факт существования сущности «асинхронный экшон».
> Flow в помощь.
Ну так для этого код должен быть такой, чтобы его можно было без болей типизировать. У вас результат createAction принимает data какого-то произвольного типа, как сделать, чтобы он был конкретного типа (пейлоад вашего экшона), и чтобы в редьюсере потом при определении соответствующей функции этот тип автоматически использовался? Я не вижу способа. Это и есть основная проблема дизайна редакса (ну, кроме упомянутой выше с асинхронными экшонами). Есть экшон криейтор, есть редьюсер, они имеют единую зависимость (чисто по смыслу) — это экшон, но его по факту в коде по дефолту нет. В итоге эту проблему надо решать — либо отдельно выписывать экшон и на него явно ссылаться (вариант дефолтного редакса), либо делать какюу-то обвязку, которая прибьет редьюсер к экшону (как у меня выше).
То что мы обсуждаем — и есть обычное использование инструмента для решения задачи. Мне непонятно, почему вы готовы писать обычные функции в реакте, но против того, чтобы делать то же самое в ангуляре в точно том же самом контексте.
> Вот что мешало включить всю конфигурацию декоратора в тело класса?
То, что это просто нельзя сделать сколько-нибудь осмысленным образом. Те метаданные, что имеет смысл наследовать, наследуются. А те, что наследовать нельзя — не наследуются. Ни стили, ни темплейты, ни другие подобные вещи наследовать просто нельзя, это чревато серьезными проблемами. То есть, эту фичу невозможно сделать так, чтобы она не была заведомо поломанной.
> Мнимая «переиспользуемость» классов отдельно от этих декораторов?
Вот, кстати, да. Если желаете — можете вынести конфигурацию декоратора и переиспользовать ее в разных классах (наследниках, например). И никаких «кастомных декораторов».
> Не притворяйтесь, вы прекрасно поняли задачу.
Нет, не понял. Ваш пример на реакте был уже позже, я когда его увидел — то переделал.
> Мой посыл в том, что дизайн кривой, пользоваться неудобно, проблемы есть.
Удобство — это субъективная категория. Я не вижу какой-либо разницы по сравнению с реактом. Мне, например, jsx не удобен и кривое разделение ответственности (ну тут, собственно, проблема в том, что реакт — не фреймворк, а библиотека, и для нормальной работы так же как и редакс предполагается дорабатывать его напильником, так что все так и должно быть).
> Нет, не сродни.
Это ваше мнение.
> Мы говорим о наследовании, а не о композиции. Если ButtonWithIcon наследует Button, то подразумевается что все поведение, вплоть до работы с EventEmitter наследуется, а не копипастится.
Во-первых, оно и наследуется. Не наследуется шаблон, потому что его нельзя осмысленно наследовать, а ивентэмиттеры и т.п. — наследуются, я же об этом выше уже говорил. Во-вторых, нет, это именно композиция. Вы пишите одну компоненту, в которой вызывается другая компонента, естественно вы передаете во вторую компоненты требуемые ею аргументы, точно так же как вызывая функцию из другой функции. Это происходит на уровне controller/view (шаблона). А на уровне модели (ComponentClass) вы наследуете. В реакте модель гвоздями прибита к вью (их можно разделить, вообще, но сам реакт по дефолту не о том), в ангуляре — это две разные и независимые сущности. По-этому наследуются метаданные которые относятся к модели (соответствующие декораторам членов), но не наследуются те, что относятся к виду Это же простая и ясная структура. В чем проблема?
> Что дизайн оправдан? Нет.
Это ваше мнение. Единственную вашу объективную претензию мы рассмотрели, если других таких же нету — я не вижу смысла продолжать. Потому что «здесь вызов функции — использование инструмента, а там — костыль» или «оно врапает а я хочу чтобы не врапало» — это не разговор.
Не думаю, обычно если что-то с виду не нравится — то оно не нравится. У меня вот как кровь текла из глаз от jsx'a, так и продолжает течь, и ничего не сделаешь.
Ну вы согласны тогда, что писать свои метаредьюсеры нецелесообразно? И hoc в реакте — тоже? Если да, то тогда ваша позиция понятна. Если нет — то тогда тут наблюдается явная непоследовательность.
> По кругу мы ходим из-за проблем ненаследования метаданных, а не наследования inputs/outputs.
Тогда к чему вы приплели inputs/outputs?
> вам нужно пойти во все наследники и руками пробросить это все в шаблоне (как в случае с пробросом clicked).
Это сродни претензии к тому, что если вы в функцию добавили аргумент, то надо везде, где эта функция вызывается, добавить этот аргумент. Я тут ничего плохого не вижу.
> Кроме того, если у кнопки есть стили, а в 99% случаев они есть, то они не наследуются.
Так это правильно. Они легко могут сломаться, если их отнаследовать.
> Совсем не то же самое. Иконка должна быть внутри my-button.
Так бы сразу и сказали, я же не могу мысли читать:
так?
Декоратор — и есть просто функция. Мы, вроде, уже это выяснили?
> Это все очень и очень здорово. Только вот, чтобы получить кнопку с иконкой, ни декораторы, ни HOC не используются:
ну вот мой вариант был:
то же самое ведь?
> Мы ходим по кругу.
Нет, они сами по себе наследуются. Без «кастомных декораторов» и всего такого:
Так я этот бойлерплейт не запаковываю — он есть, я могу использовать любой его фрагмент. Я только не пишу его руками (но если будет надо в каком-то специфическом случае — могу и написать). Просто в оригинальном виде приложение на редаксе поддерживать невозможно в принципе. Собственно, сам Абрамов тоже пишет, что редакс — легковесная библиотека, которая предполагает использования разного рода обвязок для конкретного проекта. Примеры он на StackOverFlow приводил.
Я тоже использую ngrx/store. При этом у меня есть обвязка из (только не пугайтесь)… кастомных декораторов, которая уменьшает типичный редаксовский бойлерплейт в разы ;)
генерит сразу action-константу, action-creator, метод с вызовом dispatch'a и соответствующий кусок редьюсера. И, да, в отличии от декораторов класса, декораторы аргументов и методов, конечно, уже не просто функции.
Инпуты/аутпуты наследуются. У меня же выше был пример с наследованием, там это видно.
> Кастомный декоратор — это сторонние средства.
Декоратор — это _просто функция_. Вот это:
ровно то же самое, что и вот это:
Когда вы на реакте применяете какую-то ф-ю к компоненту — вы фактически и используете декоратор :)
то есть «определить и использовать в реакте HOC» и «определить и использовать кастомный декоратор» — это _одно и то же_. HOC из реакта — это _и есть_ декораторы классов.
> что стандартный Component использовать нельзя, нужно использовать кастомный. И мы возвращаемся к огромному бессмысленному мануалу.
Команда же может запомнить, что надо юзать higherOrderComponent выше? :)
> Окей, тут соглашусь
Значит, по факту декларативности мы вопрос утрясли.