> Нет, в зависимости от состояния этого промиса, результаты будут разные.
С чего бы это? Мы же не делаем await промиса в редьюсере, редьюсер только создает промис, но не ожидает его завершения. Потом в then промиса диспатчится какой-то экшон и уходит в стор. В итоге у нас в стор в обоих случаях приходит вполне детерменированно два одинаковых экшона в одном и том же порядке.
> Он не должен что-то там спавнить по ходу, так как сразу обрастет сайд-эффектом.
Этот сайдэффект все равно есть. И от того что вы его отодвинули ничего не меняется — ну это просто заметание под ковер.
> И я не очень понимаю, почему выполнение эффекта вне редьюсера вызывает столько негодования.
Потому что это неудобно. Вот и все. И при этом ничего не дает.
> Держа редьюсеры чистыми, вы на халяву получаете простоту отладки, простоту тестирования, тайм-тревелинг, undo/redo и прочее-прочее.
В описываемом мной случае все точно так же — у нас линейная последовательность экшенов, которую можно отматывать и так далее.
Надо просто во все инпуты, где пробрасываются именно данные (а не константы/конфиги) делать по дефолту обсерваблами (и, с-но, асинк в темплейте). Это даже не то что для таких вот случаев, а в принципе архитектура ангуляра заточена больше под такой flow, он в целом будет удобнее (хотя поначалу и непривычно).
Вполне детерменирован — он же вернет в точности тот же результат. Просто по пути заспавнит экшон, который придет потом в стор. В точности то же самое и в приведенном выше решении — просто экшен заспавнится несколько раньше. Единственное, что нам нужно — чтобы сама последовательность экшенов не нарушалась — ведь состояние стора больше ни от чего не зависит! Если последовательность обработки экшенов неизменна — то и состояние стора на каждом шаге будет тем же. А когда и как запускаются промисы — вобщем-то не важно, это деталь реализации, которая не влияет на результат.
Ну это странное мнение, учитывая что attribute directives предназначены _именно_ для решения таких проблем. То есть вот он инструмент специально для этого заточенный.
> UPD: Кстати, а HostBinding поддерживает async pipe?
В каком смысле? Пайпы же находятся в темплейте. Если вы хотите сделать так, чтобы какоето проперти, которое использовалось без асинка, начало использоваться как бы с асинком — то насколько я знаю, так нельзя, надо делать subscribe и менять обычное значение. Но если оно и так уже было с асинком — то вы можете засунуть туда observable.
Я не понял. И так, у вас есть компонент, у компонента какие-то пропсы. Вы хотите сделать в точности такой же компонент с таким же видом, но чтобы в нем не было пропсов, а он сам подгружал данные (с сервера, редаксом, еще как -либо — не важно)? Все так? Тогда делаете attribute directive и выставляете инпуты хоста через @HostBinding, нет?
> Проблема в тестируемости и потере детерминированности. Засунете вы в стор не промис, а декларативное описание этого промиса.
Ну да, промис же автоматом стартует при создании, так что описание промиса а не промис. Ну это не так важно, речь о том, что разницы-то никакой нет, тогда зачем огород городить? Ну стартанет твой промис потом, сразу после выполнения редьюсера. Ну и что? что это дает? Нам единственное что нужно — это чтобы следующий экшон не был обработан редьюсером до того, как будет обработан предыдущий, чтобы поток выполнения был линеаризован. И все.
Если он за 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) вы наследуете. В реакте модель гвоздями прибита к вью (их можно разделить, вообще, но сам реакт по дефолту не о том), в ангуляре — это две разные и независимые сущности. По-этому наследуются метаданные которые относятся к модели (соответствующие декораторам членов), но не наследуются те, что относятся к виду Это же простая и ясная структура. В чем проблема?
> Что дизайн оправдан? Нет.
Это ваше мнение. Единственную вашу объективную претензию мы рассмотрели, если других таких же нету — я не вижу смысла продолжать. Потому что «здесь вызов функции — использование инструмента, а там — костыль» или «оно врапает а я хочу чтобы не врапало» — это не разговор.
С чего бы это? Мы же не делаем await промиса в редьюсере, редьюсер только создает промис, но не ожидает его завершения. Потом в then промиса диспатчится какой-то экшон и уходит в стор. В итоге у нас в стор в обоих случаях приходит вполне детерменированно два одинаковых экшона в одном и том же порядке.
> Он не должен что-то там спавнить по ходу, так как сразу обрастет сайд-эффектом.
Этот сайдэффект все равно есть. И от того что вы его отодвинули ничего не меняется — ну это просто заметание под ковер.
> И я не очень понимаю, почему выполнение эффекта вне редьюсера вызывает столько негодования.
Потому что это неудобно. Вот и все. И при этом ничего не дает.
> Держа редьюсеры чистыми, вы на халяву получаете простоту отладки, простоту тестирования, тайм-тревелинг, undo/redo и прочее-прочее.
В описываемом мной случае все точно так же — у нас линейная последовательность экшенов, которую можно отматывать и так далее.
Вполне детерменирован — он же вернет в точности тот же результат. Просто по пути заспавнит экшон, который придет потом в стор. В точности то же самое и в приведенном выше решении — просто экшен заспавнится несколько раньше. Единственное, что нам нужно — чтобы сама последовательность экшенов не нарушалась — ведь состояние стора больше ни от чего не зависит! Если последовательность обработки экшенов неизменна — то и состояние стора на каждом шаге будет тем же. А когда и как запускаются промисы — вобщем-то не важно, это деталь реализации, которая не влияет на результат.
Ну это странное мнение, учитывая что attribute directives предназначены _именно_ для решения таких проблем. То есть вот он инструмент специально для этого заточенный.
> UPD: Кстати, а HostBinding поддерживает async pipe?
В каком смысле? Пайпы же находятся в темплейте. Если вы хотите сделать так, чтобы какоето проперти, которое использовалось без асинка, начало использоваться как бы с асинком — то насколько я знаю, так нельзя, надо делать subscribe и менять обычное значение. Но если оно и так уже было с асинком — то вы можете засунуть туда observable.
Ну да, промис же автоматом стартует при создании, так что описание промиса а не промис. Ну это не так важно, речь о том, что разницы-то никакой нет, тогда зачем огород городить? Ну стартанет твой промис потом, сразу после выполнения редьюсера. Ну и что? что это дает? Нам единственное что нужно — это чтобы следующий экшон не был обработан редьюсером до того, как будет обработан предыдущий, чтобы поток выполнения был линеаризован. И все.
> Вспомните саги — ровно то же.
Нет, сага — это свободная монада, интерпретатор.
Если он за 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) вы наследуете. В реакте модель гвоздями прибита к вью (их можно разделить, вообще, но сам реакт по дефолту не о том), в ангуляре — это две разные и независимые сущности. По-этому наследуются метаданные которые относятся к модели (соответствующие декораторам членов), но не наследуются те, что относятся к виду Это же простая и ясная структура. В чем проблема?
> Что дизайн оправдан? Нет.
Это ваше мнение. Единственную вашу объективную претензию мы рассмотрели, если других таких же нету — я не вижу смысла продолжать. Потому что «здесь вызов функции — использование инструмента, а там — костыль» или «оно врапает а я хочу чтобы не врапало» — это не разговор.