Comments 182
>node
> 0.1*0.1
0.010000000000000002
> 0.1*0.2
0.020000000000000004
> 0.1*0.3
0.03
> 0.1*0.4
0.04000000000000001
> 0.1*0.5
0.05
> 0.1*0.6
0.06
> 0.1*0.7
0.06999999999999999
> 0.1*0.8
0.08000000000000002
> 0.1*0.9
0.09000000000000001

У меня все.
Что все? Это IEEE 754, во всех языках, которые его используют так будет.

А статья — очередное «вступление в мир моды на быдлокод в JS», которых, как сам автор сказал, уже очень много и потому не нужна
… и long int или его аналог. В отличие от JS, в котором все, что не помещается в 4 байта преобразуется в double:
console.log(12345678999999999-12345678999999998); // 2

В JS — всё double, тут никаких int нет by design, если не считать объектные расширения

Да, извиняюсь, был неправ. Видимые отличия от использования целочисленных типов начинаются с 54 бит.
golang:
package main
import "fmt"

func main() {
	var a float32 = 0.1
	var b float32 = 0.1
	fmt.Println(a * b)
	b = 0.9
	fmt.Println(a * b)
}

> 0.010000001
> 0.089999996
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

1> 0.1 * 0.1.
0.010000000000000002
2> 0.1 * 0.2.
0.020000000000000004
3> 0.1 * 0.3.
0.03
4> 0.1 * 0.9.
0.09000000000000001

Поздно заметил, что вы складываете. А вообще, никаких неожиданностей
writeln(0.1*0.2 == 0.02f); //false
И все равно не понимаю, что вы пытаетесь доказать? Что операции с числами с плавающей точкой в D не соответствуют стандарту IEEE754?
writefln("%1.18e", 0.1*0.2); //2.000000000000000042e-02

Нет, что стандартная библиотека D достаточно умна, чтобы округлять последние биты при выводе. Всякие "0.020000000000000004" пользователю как правило не нужны.

В данном случае даже опаснее доверять выводу writeln, так как:
import std.stdio;

void main() 
{
	double a = 0.1;
	double b = 0.2;
	if(a + b == 0.3) {
		writeln(true);
	}
	else {
		writeln(false);
	}
}

> false

Да и в целом в любом языке так, все случае не предугадаете, когда будет 0.3, а когда 0.3000000001
Поэтому в любом случае стоит делать проверку на диапазон или любой другой хак.

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

Я просто хотел сказать, что для каких-то важных, точных вычислений, JS абсолютно не годится. Вообще, числа с плавающей запятой — это отдельная тема.
Он не годится в той же мере, в какой не годятся вообще все вычисления в формате IEEE754 на всех платформах и ЯП.
> Первую задачу решает react-router. Здесь альтернатив нет
react-router-redux?
Разве эта библиотека — не построена поверх react-router? Мне казалось, что она просто дополнительно пропускает переходы через store, чтобы сохранить time travel. Нет?
Ну она синхронизирует текущую позицию в store и обратно — полезно для Time Travel, доступа к данным метонахождения через Redux, что позволяет компоненту в ряде случаев не зависеть от роутера. Но это совершенно точно не «альтернатива» :)

(Более чем) реальная альтернатива react-router-у — junctions.js.
Как короткое описание своего технологического стека c возможностью изучать тему «вглубь» по ссылкам — статья понравилась.
Немного критики. К сожалению, ряд моментов режет глаз.
Сравним с бекендом: MVC-фреймворк, ORM, Data Mapper, IOC-контейнер...
Почему MVC? Почему фреймворк? Почему ORM? И т.д. Это вовсе не необходимые компоненты бэкенда. Я бы назвал их «модными», но бэкенд может быть построен на совершенно других принципах. Кмк, такое сравнение тут не совсем к месту.
Самым монструозным из всех был конечно Smarty. Мне казалось, что люди сошли с ума. Как иначе можно было объяснить желание написать шаблонизатор… для шаблонизатора Perl?
Конечно же автор имеет в виду php, а не perl. Фраза о «шаблонизаторе для шаблонизатора» тоже модная, но слишком часто используется не в тему.
Автор действительно имеет в виду Perl — первая версия PHP была написана на Perl и под его сильным влиянием. $ перед каждой переменной? Это из Perl!
Почему MVC? Почему фреймворк? Почему ORM? И т.д. Это вовсе не необходимые компоненты бэкенда. Я бы назвал их «модными», но бэкенд может быть построен на совершенно других принципах. Кмк, такое сравнение тут не совсем к месту.

Потому что примерно 90% кода из мира веб-разработки, который я видел или поддерживал — это MVC-фрейворк + ORM и не более. На PHP — это чаще всего Active Record, на Java — Hibernate, на .NET — Entity Framework. Да, бывают другие стеки. Но мейнстрим — именно ORM + MVC = love

Как по мне, то для разработчика MV* ООП веб-бэкендов, не стоит начинать свой путь во фронте с React+Redux, достаточно одного React, или React+MobX, если практикуешь DDD. Redux — это полное разделение состояния и поведения, а не их инкапсуляция в объектах.

Action'ы redux'а — это же не доменные объекты, а скорее просто message. Что мешает ловить экшны, создающие эффекты в middleware, обрабатывать в любом удобном виде, в т.ч. с применением DDD и по завершению операции выбрасывать DomainEventSucceeded / Failed и уже их обрабатывать в редюсерах?

Доменные объекты инкапсулируют данные и поведение. Redux же их разделят по крайней мере на уровне рекомендуемых практик. Может и можно технически хранить в store полноценные объекты с методами, реализующими доменное поведение, а потом дергать их в редюсерах, но точно это будет шоком для подавляющего большинства активно использующих Redux. А скорее всего эта возможность либо просто заблокирована (может только в дев-режиме), либо ожидаемой реакции на вызов методов объекта не будет, поскольку redux+react не заметят, что изменилось состояние объекта в редьюсере, поскольку ожидает либо возврат того же объекта без изменений, либо нового объекта. В общем не прокатит что-то вроде (state, value) => state.domainObject.setValue(value).


Можно отдельно вынести мутабельную модель и проецировать её состояние на redux-стор в миддлварах, но что нам это даст, кроме "потому что можем"?

А кто мешает применять DDD? Просто данные будут в сторе, а логика — отдельно — в функциях, сагах, и всяких таких штуках. Разделение ответственности же, до сих пор же идут споры о «правильном DDD» — никто не говорит, что для этого нужен один мутабельный объект модели для каждой сущности с методами поведения. Концептуально DDD никто не мешает использовать.

P.S. На практике немного сложнее, в сторе обычно находятся не только данные «domain model», а куча разного рода другого состояния приложения.

По-моему сложно будет говорить о выделенном слое бизнес-логики, всё будет в одной куче и бизнес, и инфраструктура, и UI.

Ну, этот паттерн, который называют то «saga», то «process manager» — как раз об этом. Он описывает чистую логику, результатом которой являются те или иные «action»-ы. Тем самым хранилище состояния (Redux) только тупо хранит состояния и меняет его с помощью редьюсеров, а сама логика обрабатывается специализированными функциями.

Тут не нужно придумывать отдельный паттерн для «бизнес-логики», чтобы как-то технически отделить ее от логики приложения. Просто не надо в кучу мешать (разные «саги», разные ключи в хранилище стейта, все такое).
state у редакса это готовое отображение мира для UI. О том, что состояние надо поменять как раз и говорят доменные объекты, которые умеют делать dispatch. Это в общем-то типичный CQRS+ES

DOM — это готовое отоброажение мира для UI. А state у вас — это промежуточная модель.

Он (или любая другая система хранения и управления состоянием приложения) нужен реакту, поскольку имеющаяся у него довольно примитивна.

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

Плюсы, навскидку:


  • декларативное описание целевого состояния DOM без учёта текущего на языке очень близком к HTML с возможностью использовать все средства JS
  • средства автоматического обновления DOM при изменении состояния
  • компонентная модель с высоким уровне изоляции
  • исключительно односторонний биндинг
  • довольно заметные оптимизации ререндеринга DOM
  • минимум три способа создавать компоненты (обычные функции, наследники Component, наследники PureComponent)
    Минусы:
  • нестандартный синтаксис, требующий компиляции в JS и изучения
  • невозможность создавать компоненты, представимые в DOM типами, отличными от элементов (прежде всего напрягает отсутствие возможности представить компонент списком элементов)
  • местами многословный синтаксис
  • неопределенность в плане лучших практик
  • доминирование отдельных не самых лучших в общем случае решений
декларативное описание… все средства JS

Что-то тут не так.


исключительно односторонний биндинг

Что плохого в двустороннем?


минимум три способа создавать компоненты

То есть чем больше способов создавать компоненты — тем лучше?

Что-то тут не так.

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


Что плохого в двустороннем?

Конфликты и циклы. Да даже без них — усложнение логики в виду отсутствия единого источника правды.


То есть чем больше способов создавать компоненты — тем лучше?

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

Всё так. Интерфейс сугубо декларативный, а логика вполне может быть императивной.

setState — какой-то совсем не декларативный интерфейс.


Конфликты и циклы. Да даже без них — усложнение логики в виду отсутствия единого источника правды.

Это всё особенности Ангуляра к двусторонним биндингам, в общем случае, отношения не имеющие.


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

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

setState — какой-то совсем не декларативный интерфейс.
Ну вот зачем придираетесь? Под «всеми средствами» JS, очевидно, имелся полный доступ к отрисовываемым объектам, фильры, сортировки, мерджи и т.п. при непосредственном рендеринге.

Это всё особенности Ангуляра к двусторонним биндингам, в общем случае, отношения не имеющие.
WPF и компания, видимо, в общем случае тоже к двусторонним бингингам отношения не имеют? Там это давно наболевшая проблема.

Например? Что за условия могут потребовать создавать компоненты по разному?
stateless vs stateful
Под «всеми средствами» JS, очевидно, имелся полный доступ к отрисовываемым объектам, фильры, сортировки, мерджи и т.п. при непосредственном рендеринге.

Например, взять и добавить атрибут в уже отрисованный дом?


Там это давно наболевшая проблема.

В $mol такой проблемы нет, а двусторонние биндинги есть. Что я делаю не так?


stateless vs stateful

И зачем их по разному создавать?

И зачем их по разному создавать?

Уменьшение накладных расходов

setState — какой-то совсем не декларативный интерфейс.

Он не относится к описанию целевого состояния DOM. Целевой DOM является чистой функцией от props и state.


Это всё особенности Ангуляра к двусторонним биндингам, в общем случае, отношения не имеющие.

Это общая проблема двусторонних биндингов. В Excel/Calc попробуйте двумя ячейкам друг на друга сослаться. А лучше через десяток промежуточных.


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

  • stateless компонент (чистая функция от свойств)
  • stateful компонент с "нативным" setState
  • stateful компонент со сторонним хранением и управлением состояния (redux, mobx, rxjs и т. п.)
Он не относится к описанию целевого состояния DOM. Целевой DOM является чистой функцией от props и state.

Зачем тут чистота, если достаточно идемпотентности?


Это общая проблема двусторонних биндингов.

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


В Excel/Calc попробуйте двумя ячейкам друг на друга сослаться. А лучше через десяток промежуточных.

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


stateless компонент (чистая функция от свойств)
stateful компонент с "нативным" setState
stateful компонент со сторонним хранением и управлением состояния (redux, mobx, rxjs и т. п.)

И зачем тут 3 разных способа создания компонента?

Чистота чище :)


Именно, ругается, но позволяет их создавать.


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

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


Не позволяет. Формулы с циклическими зависимостями не работоают.


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

Тут я не о сторах, а о целевом состоянии DOM — оно чистая функция от состояния.


Позволяет. Не работает, но позволяет, а ошибка в рантайме выводится.


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

Я тоже не о сторах, а о чистоте. Погоня за чистотой порождает чудовищные решения в духе "формируем новое состояние всего дома вместо обновления пары свойств у конкретных элементов".


Позволяет ввести любую формулу. Но биндинга никакого по ней не создаёт.


Что за "инфраструктура"? Объект со ссылкой на прототип? Ну офигеть инфраструктура.

Чем они чудовищные? Очень легко поддерживаются. И позволяют сделать реальное обновление парой свойств.


Формулу сохраняет — значит биндинг создаётся. Просто он не работает. Хорошо, что ошибку выводит, а не в бесконечную рекурсию уходит.


Объект с ненужными свойствами. Ну и да, разрешение методов происходит, что небесплатно.

Например, такие: https://habrahabr.ru/post/326484/#comment_10174502


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


Любое замыкание — это объект, хранящий ссылку на функцию и контекст её создания, хранящий "ненужные переменные". Проще говоря — разницы нет от слова "вообще".

Что за доменные объекты, которые знаю о dispatch в частности и о нуждах UI вообще?

про нужды UI знает reducer(в терминах cqrs+es это был бы проектор), доменный объект знает только о том, что что-то произошло

Объект знает, что с ним произошло и даже может сообщить кому-то если спросят. Как об этом должен узнать redux, чтобы вычитать изменения в графе домена, создать соответствующие action и диспетчеризировать их?

Redux вообще не должен ничего «знать, чтобы диспетчеризовать». Его задача — только хранить состояние. У вас должен быть отдельный кусок программы, ответственный за перехват тех или иных action'ов и за то, чтобы на это как-то отреагировать (в том числе взять текущий стейт, проанализировать его и, вероятно, запустить какие то экшены).

Ну нужно пытаться вешать на Redux то, что выходит за рамки его ответственности.

Его задача не только хранение состояния (обычный JS-объект с этим справится), но и управление им.

Ну, скажем так, его задача наметить паттерн для этого (dispatch, actions, middlewares, reducers), но не обязательно предлагать непосредственный механизм для этого (вместо него — предлагается подключать что-то свое как middleware).

UPDATE: Не то, чтобы это хорошо или плохо — с одной стороны, это дает больше гибкости, но с другой — все делают кто во что горазд :) Особенно когда сложность приложения выходит за пределы разумности применения простых вещей вроде redux-thunk (который, вроде как стартовая точка и он же — первый middleware, с которым все сталкиваются).

Мешает экпоненциальная костыльность "современной front end разработки" :-)

Для себя как бэкенд-разработчика я не увидел ответа на один очень важный вопрос: зачем это всё? Почему вдруг стало нельзя просто рисовать HTML на бэкенде и отдавать его браузеру как есть (с AJAX или без)?


(Не считая случаев, когда сайт таким образом написать в принципе невозможно и нужен интерактив на JS, но ведь такие сайты по пальцам можно пересчитать)

Как бывший фронтенд разработчик, который ныне сидит на бэке, отвечаю =)
1. Рисовать HTML на бэкенде все еще можно, но такой подход не умеет решать ряд важных проблем. Например, если юзер взаимодействует с элементом, который меняет контент во многих разных частях приложения, скажем, отредактировал свое имя, которое должно успешно замениться повсюду на странице.
2. Для отрисовки на бэке тратятся ресурсы networking'а, и это имело бы смысл, будь генерация HTML'а чем-то сложным и непроизводительным, но это совсем не так — любой современный шаблонизатор работает с ast, и на клиенте это все будет работать во многих случаях быстрее чем один только пинг до сервера.
3. Отрисовка кусмана HTML'а на клиенте — это более трудозатратный процесс, чем использование DOM API, как раз потому, что снова выделяются ресурсы на парсинг и валидацию. Современные фреймворки/либы стараются использовать virtual dom, чтобы по возможности оптимизировать эти процессы.
4. Еще, конечно, обилие мобильных клиентов требует уменьшения трафика. Готовый HTML практически всегда будет весить больше чем JSON API.

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

После изменения имени страница классического сайта просто перезагрузится :)


на бэке тратятся ресурсы

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


Отрисовка кусмана HTML'а на клиенте — это более трудозатратный процесс

Видимо, разница в пределах погрешности, потому что видимой на глаз разницы в производительности между сайтами, активно юзающими innerHTML (PJAX/Turbolinks какие-нибудь), и сайтами на React не замечал (и на мобилках тормозят примерно одинаково).


Прикиньте, сколько в мире есть CRMок, облачных приложений

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

После изменения имени страница классического сайта просто перезагрузится :)
Заставив нас ждать пинга сервер-клиента, рендера на сервере, рендера на клиенте, репейнта… А в нашем случае, может быть, надо было только два слова всего поменять. Конечно, раз на раз тут не приходится, но сценарий, когда действие пользователя меняет несколько достаточно удаленных друг от друга частей сайта, но незначительно — в моей практике встречался слишком часто.

на бэке тратятся ресурсы
Вы вырвали цитату из контекста. Привожу полную цитату:
Для отрисовки на бэке[ПАУЗА] тратятся ресурсы networking'а

Ресурсы бэка, конечно, не особо тратятся (зависит от). Но вот когда сервер в Лондоне, а юзер в Монголии…

видимой на глаз разницы в производительности [...] не замечал
Зависит от сайта, разумеется. Мне доводилось замечать такую разницу на всякого рода b2b-ресурсах.

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

Я лично считаю, что серебряной пули нет. Реакт не панацея (и мне лично вообще Vue больше нравится например), и толкать его повсюду — это та же мода на сложение чисел с помощью jquery только в профиль. Если ваш фронт, которому надо сделать лендинг для разовой акции, тянет туда реакт, возможно, он не очень компетентен. Если ваш бэкендер, который пилит условный trello, морщится при слове реакт и хочет все сделать на GWT/ASP.NET — про него можно сказать то же самое.
А в нашем случае, может быть, надо было только два слова всего поменять

Если ограничиваться только этим случаем, то получается что-то вроде document.querySelectorAll('.js-user-name').forEach(x => x.textContent = newName), и React тут как-то ни к чему)


в моей практике встречался слишком часто.

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


тратятся ресурсы networking'а

В веб-приложениях всю экономию с лихвой компенсируют громадный размер js-бандлов, а при частых обновлениях кода даже браузерный кэш становится бесполезен. Зажатый gzip'ом html-код неплохо передаётся даже по EDGE (а там пинг, напомню, приближается к секунде), так что не вижу смысла экономить на спичках

Ну, вы как-то даже и не хотите видеть каких-то преимуществ SPA-приложений (а люди с вами зачем-то спорят). Если вам хватает ваших инструментов — у вас же их никто не отбирает. У всех задачи опять же разные. Всякие чисто «хайповые» штуки со временем уходят или находят свою нишу, а вот полезные паттерны — остаются. Неужели вы думаете, что все и каждый вокруг вас — хипстер, которому нравятся модные словечки и использует такой подход только из за своей хипстеровости (есть такое слово?) :)

Возьмем тот же Реакт и его серверный рендеринг — и вот вам вполне себе альтернативный вариант написать чисто серверный UI (можете даже не подрубать React на клиенте). Кому-то удобнее, кому-то нет, но это еще один полезный паттерн.

document.querySelectorAll('.js-user-name').forEach(x => x.textContent = newName)

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

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

Ну, я именно это имел в виду под «хранением и управлением состояния». Надо было добавить «и корректному отображению всего этого на UI». И тут становится понятно, что и virtual DOM не просто так придумали и всякие другие штуки для этого — чтобы была возможна концепция «UI — это функция от состояния». Но автор комментариев выше настаивает на том, что все это не нужно, и я, наравне со многими другими, с этим несогласен :)
Если ограничиваться только этим случаем, то получается что-то вроде document.querySelectorAll('.js-user-name').forEach(x => x.textContent = newName), и React тут как-то ни к чему)

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

на некоторых страницах

А вот тут всплывает другая проблема: я как пользователь хочу, чтобы контент на страницах, которые я не трогаю, не менялся, и если что-то там пересортируется само по себе без явного нажатия мной F5, я сильно расстроюсь. А если на текущей странице нужно изменить что-то большее чем пару лейблов, то всё ещё нетрудно просто перезагрузить её (почти) целиком, «ресурсы networking'а» в 2017 году уже не проблема. Так что по моему личному мнению никаких действий предпринимать не нужно :)

Вы своим кодом как раз поменяли все имена пользователей в той таблице.

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

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

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

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

В шапке на всех страницах сайта отображается имя пользователя. Тут же можно его поменять (почему бы и нет?). В теле страницы "обсуждаем завтрашнюю стрелку" выводится список список "ребят с вашего двора", отсортированный по заданному пользователем критерию (имя, возраст, репутация, сила). В случае, если пользователь изменил имя, и на экран сейчас выводится список пользователей (а он может быть скрыт) и этот список отсортирован по имени пользователей, то в этом и только в этом случае нужно найти строку с текущим пользователем и переместить её в правильное место.

Это дело всё ещё нетрудно перезагрузить. Всю страницу или только список подгрузить через AJAX — неважно, но я не вижу смысла как-то ещё усложнять фронтенд в данном случае. Даже если рассматривать экономию трафика и времени на парсинг html, она получается микроскопическая (даже если список большой — напомню, 2017 год на дворе) и, имхо, не стоит того. А анимации не нужны (если только так не скажет заказчик, с которым спорить себе невыгодно — но это уже совсем другая история)

Здесь я отвечаю «давайте, потому что хороший сайт должен уметь работать без js» и меня закидывают помидорами и яйцами.)

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

Приложения тоже по большей части предоставляют информацию. Всё отличие — в интерактивности.

Перезагрузить-то может и не сложно, да только вот вот вы не уследите за всеми такими моментами, которые нужно предусмотреть. И будут вас долго и упорно преследовать багрепорты.


Заказчик обычно говорит "хочу быстро, модно, молодёжно, чтобы всё летало туда-сюда без перезагрузки и не дорого".

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

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

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

А новые фреймворки не для сайтов прежде всего, а для приложений.

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

Если их пихают на сайты, то можете обновлять страницу — для сайтов сервер сайд рендеринг маст хэв.

Мы научили фронтов верстать сразу в реакт-компоненты. Это избавляет от этапа интеграции с серверной шаблонизацией, например. Можно отдельно делать фронт, отдельно бек и не разворачивать у фронта бд и все что там еще нужно. Большие возможности интерактивного UI. Думаю, это основные плюсы.

Современные веб-приложения (а не сайты) стремятся к тому, чтобы запросов к серверу было как можно меньше и при этом в ответах отдавалось только то, что ещё не отдавалось. Минимизация количество запросов и их объёма.


Если знакомы с классическими клиент-серверными приложениями с десктопным клиентом, то у вас же не вызывает вопроса почему сервер не даёт клиенту сразу команды GDI (вроде так в винде это называется), рисующие данніе, а даёт только данные? Современные веб-приложения по сути являются полнофункциональным десктопным клиентом, не требующего установки и запускающегося с сетевого диска. Сначала ждём пока бинарник загрузится по сети, а потом общаемся с сервером только данными, делая всю работу UI на клиенте. А серверу всё равно, обычное десктоп приложение к нему стучится за данными, мобильное, веб, а может вообще другой сервер или CLI-утилита.

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

Т.е. в случае с flow / babel / jsx вас это не смутило, а в случае с TypeScript — смутило?


Это сильно ограничивает возможности использования шаблонов и мета-программирования.

Не очень понял, какие? Можно хотя бы пару примеров?


Во-вторых, далеко не все npm-пакеты идут в комплекте с d.ts-файлами.

А аннотацию flow конечно же все npm-пакеты поддерживают?


Короче, Flow показался проще в прикручивании.

Для Flow надо прикрутить babel + babel-flow, а для TypeScript — только typescript… но это оказалось сложней?

Опять бедных бэкендеров мучают redux-ом. Бэкендеры, не слушайте их, берите mobx для управления состоянием, иначе со временем проклянёте тот день, когда решили разобраться с фронтом. И так проклянёте, конечно, но с mobx у вас хотя бы будет на руках работающее приложение и гораздо меньше седых волос.
А что именно не нравится в редаксе? И в чем принципиальное преимущество mobx? Я когда сравнивал просто не нашел киллер-фич каких-то.

Как минимум, MobX нормально оперирует привычными многим бэкендерам полноценными объектами, инкапсулирующими данные и поведение. В Redux же, по сути, есть только структуры данных типа сишных структур или паскалевских записей и функции, связанные со структурами только в голове у разработчика. Глядя на функцию нельзя однозначно сказать на работу с какой частью стора она заточена.

Вообще-то можно. Для этого как раз и используется combineReducers. Это вопрос компоновки приложения, а не redux'а.

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

Эмм… зачем вам в редьюсере знать свой полный путь от корня стейта? Редьюсер должен оперировать только тем стейтом, который приходит в него первым аргументом.
Видимо, вы упускаете важную деталь. Редьюсер не должен знать об окружении, в котором он выполняется. Это чистая функция от стейта и экшена, приходящих в него через аргументы. Т.е. он не должен обращаться в какой-то глобальный стейт за данными.
Вот пример:
type TUser = {
	id: number,
	name: string
};
type TAuthState = {
	user?: TUser,
	loggedIn: boolean
};
const initial: TAuthState = {
	loggedIn: false
};
type TLoginAction = {
	type: 'AUTH_LOGIN',
	payload: {
		user: TUser
	}
};
type TLogoutAction = {
	type: 'AUTH_LOGOUT'
};
type TAction = TLoginAction | TLogoutAction;

const auth = (state: TAuthState = initial, action: TAction): TAuthState => {
	switch (action.type) {
		case 'AUTH_LOGIN': {
			return {
				...state,
				user: action.payload.user,
				loggedIn: true
			};
		}
		case 'AUTH_LOGOUT': {
			return {
				...state,
				user: undefined,
				loggedIn: false
			};
		}
		default: {
			return state;
		}
	}
};

//compose
type TAppState = {
	auth: TAuthState
};
const app: TAppState = combineReducers({
	auth
});


Редьюсер auth не знает ровным счетом ничего о том, где он находится в глобальном стейте app, да и не должен.

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

Значит вы не понимаете концепцию redux.

Вот пишете, недостаток. А какой в этом недостаток?
В редьюсер приходит не «какой-то стейт», а тот, с которым он умеет работать. И ему абсолютно по барабану, в корне этот стейт лежит, на 20 уровне вложенности, на полке или в холодильнике. Совершенно все-равно, все что он делает — это применяет нужный экшен к своему стейту.

Можете привести пример, когда вам нужно «знать, откуда пришел стейт»?

Он не знает его это стейт или не его. Вот в вашем примере придёт


{
    id: number,
    name: string
};

но это может оказаться не пользователь, а клиент.

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

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

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

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

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

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

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

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

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

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


Я его воспринимаю редьюсер как функцию от старого стейта и данных экшена с побочным эффектом в виде создания нового объекта для замены им старого стейта. Что замена происходит не непосредственно в нём — дань упрощения детектирования изменений — объекты гораздо проще сравнивать по ссылке, а не по значению.Это на основании анализа кода примеров на redux и его самого такое восприятие.

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

Я его воспринимаю редьюсер как функцию от старого стейта и данных экшена с побочным эффектом в виде создания нового объекта для замены им старого стейта.
Это на основании анализа кода примеров на redux и его самого такое восприятие
Вот и неверно. Все примеры redux, которые я видел, оперируют редьюсерами как объектами, сущностями, но никак не методами или функциями от старого стейта к новому. То, что это такая функция — детали реализации.
const app: TAppState = combineReducers({
	auth
});

У меня есть значительные замечания, но на таких примерах они проявляются слабо. Можно пример реального приложения с десятками моделей, где многие из них используются несколько раз, иногда с отличиями — ну то есть реальное приложение, а не обучающий ХеллоуВорлд? К сожалению, в интернете я таких примеров не нашел. В ООП такое вполне решается наследованием и композицией.

Я просто не хочу писать свой псевдокод, чтобы потом не было аргументов, что я придумываю, а хочу показать на чем-то реальном.
import Module1 from './modules/Module1'
import Module2 from './modules/Module2'

const combineModuleReducers = modules => {
  const reducers = {}

  for (let i in modules) {
    const red = modules[i].reducer
    if (typeof(red) !== 'function') {
      throw new Error('Module ' + i + ' does not define reducer!')
    }

    reducers[i] = red
  }

  return reducers
}

const modules = {
  Module1,
  Module2
}

const store = createAppStore(combineReducers(combineModuleReducers(modules)))

// код типового модуля
const mdl = {
   title: 'Мой модуль',
   reducer: (state = initialState) => {/* логика редюсера*/},
   path: '/module1'
}
Пример из реального приложения с сотнями моделей. Комбинируем редьюсеры так. Хочу услышать значительные замечания. Может быть что-то узнаю и исправлю.
export const fetchReducerFactory = (name, initialState, callback) => (state = initialState, action) => {
  const keys = Object.keys(initialState)

  for(var i = 0; i < keys.length; i++){
    const actionType = name + '/' + toUpperCamelCase(keys[i])+ '/Fetch'

    if(action.type == actionType) {
      const res = {...state}
      res[keys[i]] = Object.assign({}, res[keys[i]], {...action, isFetching: true})
      if(typeof(res['ui']) == 'object' && keys[i] == 'data') {
        res['ui'] = Object.assign(res['ui'], action.params)
      }

      return res
    }

    const actionTypeSucceeded =
      (name + '/' + toUpperCamelCase(keys[i])+ '/FetchSucceeded').replace('Data/', '')

    if(action.type == actionTypeSucceeded) {
      const res = {...state}
      res[keys[i]] = Object.assign({}, res[keys[i]], {...action, isInitialized: true, isFetching: false})
      return res
    }

  }

  return typeof(callback) == 'function' ? callback(state, action) : state;
}


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

Используется так:
mdl.reducer = fetchReducerFactory(
  moduleId, {
    ...initialState,
    moduleId
 })
Ну в опенсорсе этого всего нет по понятным причинам. Но основная мысль, что доменный слой не лежит на уровне редьюсеров. Редьюсеры — это состояние, необходимое только для отрисовки UI и работы самого приложения (всякие там роутинги, состояние сессии и т.п.).
Модели же и процессы работы с ними (у нас на сагах все) лежат совершенно отдельно. А там хоть ООП, хоть ФП, хоть что угодно, главное уметь реагировать на экшены запросов (command) и выбрасывать их обратно на обработку редьюсерами (event). Чтение же из стора идет через селекторы (query). Получается этакий CQRS.

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

Эмс. Ну на других технологиях довольно много чего лежит в опенсорсе почему-то. Так что причин реальных я не вижу.

Я понимаю, как работает Редакс, к сожалению я успел поработать с ним.

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

То есть я теперь могу написать любую ересь, а вы не можете привести аргументы, что я пишу ересь?)

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

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

Я же хотел бы увидеть «мы командой уже полгода пишем, активно реагируя на запросы пользователей и получили такой результат».
Эмс. Ну на других технологиях довольно много чего лежит в опенсорсе почему-то. Так что причин реальных я не вижу.
Под понятными причинами я имел в виду, что у нас все closed-source. Видимо, неудачно выразился.

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

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

Ах, я не понял, что вы именно про ВАШ код. Я же говорил впринципе про любой код.

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

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

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

Ну вот это и не нравится. Мало того, что данные и функция, их мутирующая в разных местах, так она ещё и во всех смыслах полностью отвязана.


А про типизацию не понял: в сторе редакса же все объекты нетипизированные, по сути исключительно литералы, нельзя же сделать где-то в редьюсере return new User(username), а надо return {username}. Или можно?

Под типизацией я имел в виду доп. инструменты в виде ts/flow. Вы объявляете тип объекта, с которым работает ваш редьюсер и тот же тип возвращает, и все. Где, в каком месте стейта, это происходит должно быть абсолютно все-равно.

Как у вас выстроено дерево редьюсеров, что он у вас работает с данными в разных местах? Не очень понимаю.
из приятного в redux4 combineReducers в ts будут использовать mapped types и не надо будет прокидывать руками типы в createStore, а тайпинги эти можно юзать уже сейчас
Можно, главное ссылки менять. Просто {...state} не удобно будет делать. А без этого не поменяется ссылка и Redux не запустит перерендер компонентов. Вам просто не нравится, что Redux функциональный.

Мне не нравится прежде всего, что связка Redux+React предполагает, что нужное состояние DOM является чистой функцией от одного параметра — глобального стора. В теории можно создавать хоть в каждом компоненте сторы как замену this.state и редьюсеры как замену this.setState(), но на практике это не просто.

DOM является функцией от `props` компонента — в ней может быть намешано все, что угодно, хотя обычно бОльшая часть — да, из глобального стора. Вы так говорите, будто это что-то плохое :)

P.S. Совершенно верно, редьюсеры можно использовать на любом уровне для любого стейта.

Никогда не доверял глобальным переменным :), а по сути стор таковой и является.

Если вы не хотите, чтобы ваши компоненты знали о структуре глобального стора, спрячьте чтение в селекторы, и у вас получится ну 100% CQRS.

Отчасти я наоборот хочу, чтобы знали :)


А вообще, глобальный стор редакса со всеми экшенами, которые могут его изменять, у меня чётко ассоциируются с ООП-антипатерном GodObject, а то и с процедурным программированием с большим применением глобальных переменных.

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

А вообще, глобальный стор редакса со всеми экшенами, которые могут его изменять, у меня чётко ассоциируются с ООП-антипатерном GodObject, а то и с процедурным программированием с большим применением глобальных переменных.
Вот и зря, так как стор — результат функциональной композиции, а не какая-то глобальная ерунда, к которой все имеют доступ и что-то в ней меняют, как в случае с god-object'ом. То, что стор — javascript объект — это детали реализации.

Знание "пути" в сторе позволяет быстрее определить, а что, собственно, редьюсер делает в терминах домена.


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

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

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

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


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

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

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

Разве у редакса есть ограничение, что изменять один участок стейта может один и только один редьюсер? Если есть, то мне надо многое переосмыслить :(

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

А кто сказал, что всем нужно именно это? Это просто другой подход. Просто mobx как решение и хранит состояние, и имеет весь инструментарий для его управления. Плюс реактивность MobX, но это немного другая тема.

Redux же очень прост, но, по сути, действительно, умеет только хранить состояние, да и все. Ему нужно что-то, что будет поддерживать весь его (потенциально) навороченный стейт в порядке и согласованности. Причем, с ростом проекта, таких сценариев становится все больше, а они сами становятся все сложнее. Нужна асинхронность, опять же. Отсюда и появляются redux-saga, redux-observable (реактивные «саги»), сам я предпочитаю вообще Cycle.js использовать для подобного рода логики.

Так что, тут просто разница подходов и, по сути, дело вкуса. Но почему то мне кажется, что Redux придется по душе многим, или даже большинству, именно из за своей прямолинейности и простоты.
А я люблю, как вы выразились «хипстерские штучки», и именно такие как я и популяризируют elm и clojure script. Найти работу под эти мощнейшие «штучки» вполне можно. А vanilla js/ts/cs/хзс, спасибо, нет. Хочется получать удовольствие, и спать спокойно.
Вчера Coffee, сегодня Elm, завтра еще что-то. Мода ради моды, которая живет только на волне хайпа.
Сдается мне вы не вникали в элм, иначе бы не сравнивали сахар с философией.

Наконец-то я нашёл эксперта по Elm! Не подскажете, как повесить на элемент пассивный обработчик события? То есть нужен аналог вот этого:


el.addEventListener( 'wheel' , onScroll , { passive : true } )
MVC-фреймворк, ORM, Data Mapper, IOC-контейнер, логер, профайлер, очереди, управление конфигурациями, сборка и выкладка

а кто сказал что все это обязательно надо?
Не надо явскриптовое безумие переносить обратно на бэкенд.

Ну а кто сказал, что на клиенте вам обязательно надо все перечисленное в статье?
Мне действительно все это требуется на фронте. Стек на беке я тоже написал среднестатистический. Очереди не везде конечно, а остальное у нас везде есть в том ли ином виде.

для новичка будет плохо, особенно, когда начинается описание того, что такое компонент, рендер, state, props. Автор преподносит автору салат из терминов философии реакта) Я то понимаю, но для новичка это будет кошмар. Ещё и сравнения вначале ненужные и интро затянутое.

Only those users with full accounts are able to leave comments. Log in, please.