Комментарии 15
list ... Было/Стало
Спорное улучшение. Вместо отчетливо видимой "UI как функции от стейта", получили какой-то конфиг, без существенной экономии кода. И это в самом базовом варианте.
Вместо отчетливо видимой
Думаю не стоит путать "отчетливо видимой" и "привычной", иногда не все привычное есть хорошо, но здесь дело вкуса и опыта, соглашусь, что для кого-то это может оказаться спорным моментом.
без существенной экономии кода. И это в самом базовом варианте.
В том то и дело, что в базом варианте экономии и не видно (для меня это не только про экономие, но и про удобство - это из рязряда нужно брать и пробовать), чем больше компонент, чем больше у него состояний, тем больше наша "экономия", + в статье писал про обертку
export const statusesList = ({ effect, source }: Store<ListState>) => {
return combine(status({effect}), source, (effectStatus, list) =>
effectStatus === 'done'
? Object.values(list).length
? 'done'
: 'empty'
: effectStatus,
);
}
....
export const $someStoreStatus = statusesList({
effect: someEffect,
source: $someStore,
});
Здесь мы можем обработать базовые исходы все, и теперь, когда в "Было" мы будем каждый компонент обмусоливать, в "Стало" вызовем list и передадим базовые кейсы, вызвав statusesList. (к слову, подобное можно реализовать в любом инструменте, дело в поддержке всего этого написанного)
Нам приходится постоянно тащить за собой в компонент useStore, и чем больше данных вам нужно - тем сильнее разрастается компонент, и это мы еще даже не обрабатываем данные...
Давайте перейдем к первому этапу - воспользуемся reflect.
Реакт постоянно тянет вывернуться на изнанку и обернуться в мир фантазий с элементами функционального программирования. Но его все равно оттуда достают. Помните HOC, mapStateToProps, а потом нет - хуки?
Effector крутой фреймворк, но то, о чем вы рассказываете в данной публикации - спорно.
В реакте принято выражать композицию компонентов с помощью вложенности тегов друг в друга. Да, все то же самое можно делать и другими способами - чисто технически это может быть даже проще, - но это противоречит главному принципу.
Композируя компоненты не через другие компоненты, а, допустим, через данные, вы делаете рендеринг в целом зависимым от каких-то сторонних, ни как не контролируемых реактом факторов. Это довольно быстро начинает выходить боком в поддержке. Начиная с невнятных стектрейсов и вплоть до трудно решаемых проблем с перфомансом из-за того что компоненты прорастают из данных когда угодно.
Отрефакторил ваше приложение используя реактивный JSX: SLOC уменьшился примерно в 2 раза, а объём зависимостей в сжатом виде в 15 раз.
Отрефакторил ваше приложение используя реактивный JSX:
А зачем?
Человек, удалив зависимости, в виде материал ui и других, почистив код и приведя его к низкому уровню, пытается выставить mol (оперируя фактами кол-ва строк кода и объема зивисимостей), зачем - вопрос. Хотя статья - туториал, и никаких сравнений здесь не должно быть)
приведя его к низкому уровню
Наоборот, к высокому. Вьюшки и модели представлены самодостаточными классами, а не лапшой из глобальных переменных. Не стоит благодарности.
На самом деле интересно, хоть и неожиданно. Любую задачу можно решить разными способами и разница в виде diff выглядит наглядно.
Для полноты картины не хватает модульных тестов и какого-нибудь сторибука. Они хорошо, как лакмусовая бумажка, показывают из каких соображений и насколько хорошо тот или иной подход позволил декомпозировать код (или же получилась красиво приготовленная лапша, где все зависит от всего).
Добавил туда и тесты.
Чистенько :) Только один вопрос. В приложении компоненты используется декларативно:
<NotesList
id='notes'
notes={ ()=> this.notes() }
/>
а в тесте создаются императивно и без таких вот пропертей:
const view = new NotesList
view.notes().make( 'foo' )
view.notes().make( 'bar' )
const dom = view.render()
Уверен, что связь там наверняка тривиальная. Но все равно не покидает ощущение, что тест тестит что-то другое. Можете добавить пример теста с jsx (или объяснить почему так делать не стоит если у вас другое мнение)?
JSX тут на выходе даёт настоящий DOM, а не виртуальный. Из него, конечно, можно получить экземпляр компонента, но зачем, если его можно сразу и создать. К тому же рендеринг зачастую и не нужен - достаточно дёргать апи компонента.
Ну а к декларативности JSX не имеет никакого отношения.
Вопреки расхожему мнению, декларативные языки описывают на самом деле не "результат", а некоторую семантическую структуру. И эта структура может быть использована для программного анализа с получением множества разных "результатов" в зависимости от потребностей
Что бы там ни говорили адепты
функционального программирования, но оно ни в коем разе не является декларативным. ФП - это просто императивное программирование на иммутабельных структурах, описывающее конкретные действия, по трансформации заданных входных параметров в заданные выходные. Всё, что можно сделать с чистой функцией - это её исполнить
Ваши трактовки отличаются от классических. Я не говорю, что вы не правы, просто непривычно.
Обычно говорят о трёх категориях моделей вычисления: https://en.m.wikipedia.org/wiki/Model_of_computation - последовательные, функциональные и конкурентные. Они используются при анализе алгоритмов и дают инсайты разработчикам языков программирования.
Языки программирования задают грамматику/синтаксис и модель исполнения https://en.m.wikipedia.org/wiki/Execution_model. Условно, это то, что представляет себе разработчик, читая программу - как она будет исполняться.
Получается, что алгоритм составляется в логике модели вычисления, а записываться с учётом модели исполнения выбранного языка.
Декларативное программирование https://en.m.wikipedia.org/wiki/Declarative_programming это стиль, когда конструкции в программе выражают чисто логику вычисления (фактически что было в исходном алгоритме), без описания потока исполнения (т.е. как посчитать).
Например, если мы говорим, что "в списке есть элемент foo и элемент bar", то декларативной можно трактовать запись:
<NotesList>
<Node>foo</Node>
<Node>bar</Node>
</NodeList>
Если алгоритм был бы задан иначе: "в пустой список добавили элемент foo, а потом - bar" - тогда декларативной будет вот такая запись:
const notesList = new NotesList
notesList.make( 'foo' )
notesList.make( 'bar' )
Мы отклоняется от декларативности в ситуациях когда язык не позволяет выразить алгоритм в своих терминах, либо мы сами выбираем для него иные выразительные средства. Это может делаться сознательно, например с целью оптимизации, либо безосновательно.
За пределами алгоритма, ни какой другой семантики декларативный стиль не рассматривает. Все остальные интерпретации это уже область наших оценочных суждений, а не программирование.
Моделей вычислений, которые относят к ФП несколько. У большинства ноги растут из математики. В популярной lambda calculus программа составляется как композиция функций (f.g) и так далее. Данные здесь вещь вторичная, как вам неважно при записи математикой формы X там или Y. Есть даже point-free style, где сам агрумент функции ни разу упоминается. Вычислять все это можно, например, как редукцию функции (упрощение, переписыванием из одной формы в другую, как на уроках матана).
В JS мы в самом деле вынуждены использовать императивые конструкции когда пишем в стиле ФП. Но это вынужденное отклонение от декларативности, из-за ограничений модели вычисления в этом языке, а не свойство исходной модели вычисления. В том же Haskell картинка ровно наоборот - ФП как родной, а императивность и энергичность приходится костылить.
Вы говорите не про декларативность, а про идиоматичность.
Что примечательно - каждый под декларативностью понимает что-то своё. Это верный признак нечёткого определения. А википедия только ещё больше всех запутывает. Английская явно написана "функциональщиками, которые очень хотят называться декларативщиками". Русская в этом плане менее подвержена набегам сектантов: https://ru.m.wikipedia.org/wiki/Декларативное_программирование
Так сложилось, что айти развивается на английском. Там связи между теорией CS и практикой, выраженной в конкретных языках программирования, благодаря общей терминологии выглядят довольно очевидными и само собой разумеющимися.
На русском оригинального - капля в море. Обычно это переводы, они вторичны и нередко содержат ошибки, которые переводчики однако выдают за креатив и оригинальность мысли. Где-то заменены слова, где-то пропущены детали, где-то иначе расставлены акценты и выделены какие-то несущественные детали вместо важного, добавлена эмоциональная окраска и т.п. Часто вставляют собственные трактовки, ассоциации и философию. Получается как кривое зеркало - где-то более, где-то менее - но как результат разобраться что к чему в этих отражениях гораздо сложнее. Что касается CS, я не очень доверяю Вики на русском, и стараюсь перепроверять.
Продолжаю славную традицию.
Effector — стейтменеджер js приложений (reflect, typescript, forms). Работа с основными инструментами. Часть 2