All streams
Search
Write a publication
Pull to refresh
@Druuread⁠-⁠only

User

Send message
> Нет, в зависимости от состояния этого промиса, результаты будут разные.

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

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

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

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

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

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

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

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

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

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

В каком смысле? Пайпы же находятся в темплейте. Если вы хотите сделать так, чтобы какоето проперти, которое использовалось без асинка, начало использоваться как бы с асинком — то насколько я знаю, так нельзя, надо делать subscribe и менять обычное значение. Но если оно и так уже было с асинком — то вы можете засунуть туда observable.
Я не понял. И так, у вас есть компонент, у компонента какие-то пропсы. Вы хотите сделать в точности такой же компонент с таким же видом, но чтобы в нем не было пропсов, а он сам подгружал данные (с сервера, редаксом, еще как -либо — не важно)? Все так? Тогда делаете attribute directive и выставляете инпуты хоста через @HostBinding, нет?
То есть, он все-таки отвечает за уи?
> Проблема в тестируемости и потере детерминированности. Засунете вы в стор не промис, а декларативное описание этого промиса.

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

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

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

> Тогда как контейнер не отвечает за ui.

Если он за ui не отвечает, то он и разметки содержать не должен, а значит — это attribute directive, а не component:
> Attribute directives—change the appearance or _behavior_ of an element, component, or another directive.
> Вы на что намекаете? Что удалять лишний враппер — это ок. А добавить — нет?

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

> Увы, есть.

Не совсем понял ваше объяснение. :host селектор проблему не решает?
> 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 и продолжают

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

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

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

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

Ой, фу.
> Вы считаете целесообразным лепить 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) вы наследуете. В реакте модель гвоздями прибита к вью (их можно разделить, вообще, но сам реакт по дефолту не о том), в ангуляре — это две разные и независимые сущности. По-этому наследуются метаданные которые относятся к модели (соответствующие декораторам членов), но не наследуются те, что относятся к виду Это же простая и ясная структура. В чем проблема?

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

Это ваше мнение. Единственную вашу объективную претензию мы рассмотрели, если других таких же нету — я не вижу смысла продолжать. Потому что «здесь вызов функции — использование инструмента, а там — костыль» или «оно врапает а я хочу чтобы не врапало» — это не разговор.
Типизации пейлоада же там нет? Значит, счастье не достигнуто. Ну и проблема middleware остается.

Information

Rating
Does not participate
Registered
Activity