Комментарии 106
В результате в лидеры выбились Redux, MobX и Effector.
Для принятия окончательного решения с использованием каждого инструмента было написано одно и то же крохотное приложение, после чего участники сравнили получившийся код и проголосовали за вариант, который понравился больше всего. Так мы выбрали Effector.
Серьезно? Покажите пожалуйста конкретный код этих коротких приложений, прям очень интересно, это как же надо написать код с MobX'ом чтобы предпочесть ему Effector... Сдается мне, что тот кто писал это приложение с MobX'ом вообще либо первый раз с ним знаком, либо не понимает в чем его суть и как нужно писать код используя MobX.
В голове не укладывается, как вот это



Променять на не это.
Вот например тут есть и фетчинг данных и подписки на события т.п.
https://stackblitz.com/edit/vitejs-vite-ffchmx?file=src%2Fpages%2Fmain%2Findex.tsx&terminal=dev
Бонусом там же ещё сделал чтобы все асинхронные функции в классе имели реактивные свойства fetching, error, callsCount (лежит в helpers/makeReactive)
Здравствуйте. Я рассчитывал получить от Вас комментарий =)
Не планирую оспаривать Вашу точку зрения, ведь как я говорю в статье - выбор Effector'а был ошибкой, которую мы осознали и приняли.
Тем не менее интересно, что в Вашем примере есть такая же проблема, которую мы допустили в нашем - он слишком простой, чтобы из него делать какие-то выводы.
const incr = createEvent();
const counter$ = createStore(0)
.on(incr, value => value + 1);
const handleTitleChange = createEvent();
const updateTitle = handleTitleChange.map(e => e.target.value)
const title$ = createStore('title')
.on(updateTitle, (_, value) => value);
Учитывая, что в современном FE разработчики (почему-то) избегают классы и предпочитают react-like код, я допускаю что мнения сторонних наблюдателей по поводу приведенных примеров может разделиться.
Но повторюсь - я с Вами не спорю и когда мне понадобится создать несколько таких счетчиков или сделать их логику чуть сложнее, разница станет более очевидной.
Учитывая, что в современном FE разработчики (почему-то) избегают классы
Что прям все?) Или 99%?) А если взять самый очевидный пример - Angular, там тоже классов избегают?)
и предпочитают react-like код
Опять же кто?) Да, такие люди есть, но с чего это их мнение правильное? React это view слой, да, JSX вышла удачная штука, но управлять состоянием с помощью него - рыть себе могилу сразу же.
Получается вы попали в классическую ловушку, увидели что где-то кто-то делает вот так, увидели какие-то статьи и всё, ваша картина мира сложилась и вы поняли что да, это именно то, что нужно)))
Неужели так сложно понять что это гуано не пробуя его на вкус? Например по виду и запаху? Это же сразу очевидно когда смотришь на код эффектора и на этот подход.
Так в итоге то что? Вот вам дали новый проект на работе, прям с нуля, какой стейт менеджер возьмете к нему?
Что прям все?) Или 99%?) А если взять самый очевидный пример - Angular, там тоже классов избегают?
Опять же кто?) Да, такие люди есть, но с чего это их мнение правильное?
В данном случае речь о том, что есть такой тренд есть в среде React-разработчиков (к которым мы относимся) и он достаточно популярен . Я ни в коем случае не говорю за абсолютно всех разработчиков.
Angular - очень интересный фреймворк, в разработке собственной архитектуры мы поглядываем в том числе на идеи, которые реализованы в нем и некоторые нам хорошо подходят.
Получается вы попали в классическую ловушку, увидели что где-то кто-то делает вот так, увидели какие-то статьи и всё, ваша картина мира сложилась и вы поняли что да, это именно то, что нужно)))
Примерно так и было. В статье я как раз пишу, что мы выбрали Effector отчасти "на волне хайпа". И это было ошибкой.
Неужели так сложно понять что это гуано не пробуя его на вкус? Например по виду и запаху? Это же сразу очевидно когда смотришь на код эффектора и на этот подход.
Как я упомянул в статье, мы делали выводы из слишком маленького объема информации и простого примера. Грубо говоря, "не почуствовали запах в таком масштабе".
Так в итоге то что? Вот вам дали новый проект на работе, прям с нуля, какой стейт менеджер возьмете к нему?
Мы внедряли Effector во всем ВКонтакте - это наш основной проект =) Внедрением занимались 2 десятка команд целый год. Сейчас переводим на новую архитектуру, которая для управления состоянием использует MobX.
Примерно так и было. В статье я как раз пишу, что мы выбрали Effector отчасти "на волне хайпа". И это было ошибкой.
Спасибо что не стесняетесь признавать ошибки, как большинство
Сейчас переводим на новую архитектуру, которая для управления состоянием использует MobX.
Надеюсь вам понравится, но сильно удивлюсь если вдруг нет)
В данном случае речь о том, что есть такой тренд есть в среде React-разработчиков (к которым мы относимся) и он достаточно популярен
Ну тренды штука такая, сегодня один, завтра другие) Тем более хоть убей не понимаю что плохого в этом:
class UserState {
user: IUserData = null;
authorized = false;
isAdmin = false;
fetchUserData = async () => {...}
logout = async () => {...}
}
export const userState = new UserState();
Всё лежит максимально логично и правильно, просто читай свойства и вызывай методы, что тут плохого?? Я не понимаю в чем смысл вместо этого, писать вот такого рода каракули, и это ещё только hello world пример:
const event = createEvent<EventType>();
const $store = createStore(initialValue);
const effect = createEffect((...) => { ... });
sample({
source: event,
target: effect
});
sample({
source: effect.doneData,
fn: (...) => { // трансформируем результат эффекта },
target: $store
});
Что должно случиться в жизни, чтобы прийти к такому?)
А опубликуете табличку, где MobX оказывается лучше $mol_wire?
Такой таблички нет, но некоторые концепции из $mol_wire мы какое-то время обсуждали. Пользуясь случаем задал Вам пару вопросов в комментарии к соответствующему посту тут.
Функция filter в семпле принимает на вход два аргумента, из-за чего её не всегда можно использовать как TypeGuard.
filter: ([ animal1, animal2 ]) => isCat(animal1) && isDog(animal2),
А если вот так:
filter: (p): p is [Cat, Dog] => isCat(p[0]) && isDog(p[1]),
Да, еще один хак чтоб все заработало. Указывают на то что нужно постоянно помогать эффектору чтоб все заработало
Пример в статье немного не соответствует описанию - спасибо что обратили внимание. Акцент был на том, что:
Функция filter в семпле принимает на вход два аргумента...
Т.е. проблема возникает в случаях, когда TypeGuard'ы нужны и для clock
и для source
:
sample({
clock: event,
source: store$,
filter: (value, payload) => isSmth(value) && isSmth(payload),
fn: (value, payload) => ???,
target: ...
});
Ещё немного, ещё чуть-чуть и веб разработка сделает полный аналог wpf + mvvm + reactive extensions.
P. S. Долго не мог вкурить что это за менеджеры состояний на фронте, пока не понял, что это всё было в wpf, только без хитрых названий
А ведь действительно... Хоть я и использовал менеджеры на фронте и работал с WPF некоторое время, даже интересно почему я не замечал этого сходства
Все это было в Ангуларе, но фронтендерам претят ООП, всякие архитектурные паттерны, принудительная типизация и подобное. Там сейчас тренд такой, от одного слова class плеваться, изобретать костыли поверх функциональщины, а потом гордо пытаться годами чинить то, что изначально работало до "переосмысления". Вот и получается хождение по спирали
По ходу это план «Анунаков», пересадить весь бездарный состав программистов во фронт-энд, и уже оставить эти игры с штмл + цсс конструкциями в прошлом, как и их производные штмл-конструкторы))
нефиг и сайты админить и самому их рисовать, — идите учите курсы питона и вперед «За Родину», как говорится)) . Всё, «кирдык» домашнее сайто-строение.
(Ну я фиг его знает, так чисто поболтать. «Орнул» короче)
Во-первых, благодаря «декларативности» семплов их порядок как будто бы становится неважным (хотя на самом деле он имеет значение). Это приводит к тому, что попытка переписать на них простую линейную логику может привести к появлению больших модулей, состоящих из мелких семплов. Среди них очень сложно понять, где начало и конец логического блока, особенно когда ты видишь этот код впервые. Тут легко попасть в ловушку: писать такие семплы может быть просто и даже приятно, но читать чужой код на семплах может оказаться сильно сложнее. В нашем коде встречаются модули, состоящие из нескольких десятков семплов, которые понимает только их автор.
Наверно это ключевое для меня из этой статьи, тонкости работы эффектора на нашем проекте как будто не так важны, но попытки применить эффектор в нескольких компаниях показывают, что действительно сложно поддерживать в команде практики, чтобы модели были читаемыми, в итоге всё превращается в свалку связанных между собой сущностей...
Чем дольше работаю с эффектором, тем меньше остаётся надежды в то, что это просто я не умею его правильно готовить
Вот смотрю на статью и думаю.
С одной стороны большинство разработчиков мечтает, чтобы их библиотеки использовали гиганты. Это повышает репутацию разработчика, дает возможность лишний раз рекламировать свои навыки или продукты, ссылаясь на факт доверия со стороны крупных узнаваемых брендов. С другой - компании в свою очередь не тратят время и деньги на разработку подобных велосипедов, потому что в соответствии с лицензией на код они могут юзать библиотеку по своему усмотрению. И вроде бы все ок, пока организация с известным брендом не напишет на популярном Хабре негативный фидбэк, на который впоследствии многие будут ссылаться, почему не стоит брать effector в прод. Получается, антиреклама.
И вот спустя год, в VK пришли к выводу, что библиотека не торт или возможно ее неправильно готовили. Собственно хочу спросить, а пробовал ли кто-то из VK написать issue или обратиться в чатик в телеграме (там много участников) по выявленным проблемам? Был ли положительный фидбэк от авторов или адептов? Или покупка поддержки, которая была направлена на исправление архитектурных просчетов? Я хоть далеко не любитель эффектора, но со стороны это смотрится: поматросил и бросил (публично). Была бы статья не от компании, наверное было бы другое впечатление. Популярность эффектора сыглало с ним же самим злую шутку.
А так, интересная публикация, спасибо. Есть над чем подумать и что взять на заметку )
Любой опыт от большой компании полезен, неважно позитивный или негативный. Такие статьи хорошо отрезвляют после десятков других с примитивными примерами на todo-списках. Однако верно, что негативный опыт действительно может быть объяснён по-разному - либо не разобрались, либо действительно неудачный выбор. Тогда в ход должны идти аргументы и примеры. Например в статье есть "Задача на порядок подписок", которая хорошо иллюстрирует что понимать такой код тяжело, больше похоже на птичий язык, а не JavaScript.
Случаи когда большие компании публично отказываются от инструментов нередки, например в блоге Basecamp есть статьи о причинах ухода от облачных провайдеров вроде AWS и Google Cloud.
В статье есть даже ссылки на переписку в том самом чатике. Автора спутали с небезызвестным участником хабра и кикнули за "неправильные вопросы"
Здравствуйте. Спасибо, что прочитали. Мы конечно же приходили со всеми вопросами в чат, когда не могли найти ответа в документации. В блоке с историей я рассказываю, что на этапе повторного анализа я отдельно пришел в чат со всеми моментами, описанными выше (ссылки на переписку есть в статье). В итоге для каких-то проблем мы решения нашли, но на большую часть - нет. Например, так необходимая нам динамика пока что реализуется с помощью громоздкого key-value (ждем модели), а циклические зависимости не обнаруживаются и авторы пишут что это невозможно.
Чат гораздо оперативнее и удобнее, поэтому отдали предпочтение ему перед issues. Там почти на каждый вопрос отвечали в том числе лично создатели. По многим важным вопросам они сами создавали issue. Но в наших вопросах ничего особо нового не было и они в основном относились к одному из следующих случаев:
проблема известна, мы над этим работаем (тут issue уже не нужно)
проблема легко решается / это не проблема эффектора / это не проблема вообще (тут issue как будто не имеет смысла)
Когда я вижу такие статьи, то у меня возникает вопрос: а как бы поступил Наруто Карловский?
Мне нехватает в комментариях примеров о выгодных отличиях $mol.
Я просто оставлю это здесь:
https://page.hyoo.ru/#!=3yox7h_7f2axu/View'3yox7h_7f2axu'.Details=%D0%94%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%20%D0%BB%D0%BE%D0%B3%D0%B8%D0%BA%D0%B8%20Effector
https://mol.hyoo.ru/#!section=docs/=yj0h42_ixzv4p
https://page.hyoo.ru/#!=3ia3ll_rcpl7b
Спасибо, маэстро!
Спасибо за Ваш труд. Все три материала нам знакомы и информация из них была очень полезна на этапе анализа проблем.
Я, пока читал, ожидал увидеть в конце длинный список использованной литературы, в который, помимо статей трёхлетней давности, не помешало бы добавить и чего-то свежего: https://page.hyoo.ru/#!=aylj4r_rz55bo
Ссылки на материалы, которые мы непосредственно использовали в исследованиях или при подготовке статьи указаны по месту (там, где идет соответствующее повествование).
Под "статьей трехлетней давности" Вы имеете в виду ссылку на пост про проблемы Effector? Кмк, возраст поста тут не имеет значения, он скорее призван показать как трудно было во времена нашей истории (да и сейчас) найти любую полезную альтернативную информацию конкретно про Effector.
Я скорее о том, что к началу внедрения у вас Эффектора, уже был и разбор общих проблем реактивности (40к просмотров), где он тоже участвовал, и разбор проблем конкретно Эффектора (4к просмотров), и детальный разбор более вменяемой системы реактивности (10к просмотров), и меня хантили к вам (3 просмотра). Удивительно, как вы всё это проморгали.
А причем тут Карловский? Если интересует как выглядят решения задач с использованием других стейт-менеджеров, то можно попросить написать в комментариях. Какая задача интересует?
Писал подчти о том же еще в конце 2022, но тлг каналы не гуглятся. Видимо, надо переезжать на сайт...
https://t.me/artalog/568
Действительно, выглядит как краткий пересказ большой части статьи. Жаль мы не наткнулись на этот пост раньше. С Вашего позволения добавлю в статью.
Да конечно. Еще вот очень рекомендую для личного ознакомления: https://habr.com/ru/companies/ruvds/articles/737114/
Читал Все Ваши статьи на хабре, спасибо за то что собрали в компактном виде большое количество полезной информации и интересные идеи. Моя любимая - про act :)
Конкретно в этой статье не со всеми пунктами и определениями согласен, но все равно было полезно почитать)
Да конечно. Еще вот очень рекомендую для личного ознакомления: https://habr.com/ru/companies/ruvds/articles/737114/
Уважаемый @artalar будьте так любезны ответить на комментарии:
Раз
https://habr.com/ru/companies/ruvds/articles/737114/#comment_25586780
Два
https://habr.com/ru/companies/ruvds/articles/737114/#comment_25786550
Три (Ответить нормально на этот комментарий)
https://habr.com/ru/companies/ruvds/articles/737114/#comment_25582614
А то получается что вы просто избегаете неудобных вопросов и аргументов, уже полтора года как комменты ждут ответов.
Я вижу все комментарии и не отвечаю на те, которые уже зашли в тупик, потому что кто-то ходит по кругу и не слышит ответов которые ему уже давали :)
Я вижу все комментарии и не отвечаю на те, которые уже зашли в тупик, потому что кто-то ходит по кругу и не слышит ответов которые ему уже давали :)
Come on, что за дешёвые приемы семиклассницы? Это же уровень детского сада и капризных девчонок - соскакивать с темы когда задали не удобный вопрос. В нормальном обществе принято на вопросы отвечать и на критику тоже, да. а не махать рукой и говорить "Ой всё".
Я же задал вам конкретные вопросы, на которые не получил ответа, любой может это проверить, ссылки же есть, а вы начинаете выдумывать сказки, что якобы вас не слышат и т.п. Ну т.е. типичная дешевая манипуляция для соскакивания с темы.
Мы же взрослые люди. Так что давайте, вы же уверены в себе и своих силах как разработчика? Чего вам стоит ответить внятно и конкретно на вопросы такого заурядного разработчика как я.
Итого, первый момент, ждёмс:
Раз
https://habr.com/ru/companies/ruvds/articles/737114/#comment_25586780
Два
https://habr.com/ru/companies/ruvds/articles/737114/#comment_25786550
Три (Ответить нормально на этот комментарий)
https://habr.com/ru/companies/ruvds/articles/737114/#comment_25582614
Второй момент:
Я у вас увидел в пример на сайте вот это:
https://stackblitz.com/github/artalar/reatom/tree/v3/examples/react-notifications?file=src%2Fmodel.ts
Вот с таким пафосным описанием

И вот решил повторить, но уже используя MobX
Вот что получилось
https://stackblitz.com/edit/github-ohhv1d?file=src%2Fmodel.ts
И о чудо, как же так вышло, что за таким пафосным описание скрывается элементарные вещи?
Более того, как же так вышло, что по сути с помощью тупо нативного кода всё это сделано легко и непринужденно и любой человек посмотрев на него сразу будет понимать что тут происходит, т.к. для этого не нужно вникать и углубляться с то, как работает MobX, чего нельзя сказать про reatom.
Что вы скажете по этому поводу?
Третий момент
Наткнулся на ваше видео с очень пафосным названием
Как решить главную проблему управления состояния раз и навсегда с Reatom.
Собственно вот такое название ^
И вот что получается, 20 минут вы рассказывали о "проблеме" глобального состояниях, в случаях когда нужно локальное состояние. Очередной раз вы сами придумали какую-то "проблему", которая вообще проблемой не является и не являлось никогда и грозно ее "решаете". Более того, код которым вы "решаете" данную "проблему" это конечно что-то с чем-то.
Вот вы там так же приложили код:
https://stackblitz.com/edit/reatom-computed-factory-pattern-example?file=src%2Fpages%2FSearch.tsx,src%2Fpages%2FSearch%2Fmodel.ts
А вот я его повторил, но используя MobX:
https://stackblitz.com/edit/reatom-computed-factory-pattern-example-kt19hn?file=src%2Fpages%2FSearch%2Fmodel.ts
Как говориться результат на лицо.
Что вы скажете по этому поводу?
В сухом остатки решение вашей "проблемы" такое:


И всё карл, эта вся суть "проблемы".
Или может я "опять хожу по кругу и не слышу ответов(которых нет)"?
Это какое-то чудо, вы просто повторяете уже разобранные аргументы и примеры кода. Не знаю что вам ответить.
Это какое-то чудо, вы просто повторяете уже разобранные аргументы и примеры кода. Не знаю что вам ответить.
Наверное с вами работать одно удовольствие, вам задают вопросы, а вы просто посылаете на 3 веселых буквы. Полностью проигнорировали весь комментарий и выдали рандомную отписку. Действительно, царь и не должен опускаться до того, чтобы кому-то отвечать.
Забавно, что по поводу $mol_wire у человека нет никаких вопросов. В нём что, недостатков нету?!
По Supercat Store без проблем отвечу тоже.
По Supercat Store без проблем отвечу тоже.
Он сейчас схож более менее с MobX, поэтому к нему претензий принципиальных нет и к тем, кто его возьмет для управления состоянием.
Если сравнить с MobX, я все равно бы взял MobX.
Если сравнить с Effector, Redux, Zustand, Reatom и т.п. мусором, то Supercat Store на порядки лучше их, и если выбирать между ними, я бы однозначно взял Supercat.
Далее отвечу @nin-jinв этом же треде, т.к. из-за кармы могу только раз в сутки писать
Забавно, что по поводу $mol_wire у человека нет никаких вопросов. В нём что, недостатков нету?!
API MobX'a всё же лучше и плевать мне если он весит больше.
Если сравнить с MobX, я бы взял MobX.
Если сравнить с Supercat Store, я бы взял Supercat Store.
Если сравнить с Effector, Redux, Zustand, Reatom и т.п. мусором, то $mol_wire однозначно.
Но если говорить с целом просто о $mol где всё пишется вот так:

Нет, спасибо) Да, символов меньше чем в JSX, да, пускай работает быстрее(только на уровне синтетики, в реальных приложениях нет разницы), но тут уже надо знать грань и баланс) И заплатить за это ценой такого синтаксиса так себе идея)
Я же вас про $mol_wire спрашивал, а не про $mol_view и тем более view.tree.
К тому же у вас код кривой, где вы такой взяли?
Ну и можно пример реального приложения в котором "разницы нет"?
Видимо он имеет в виду, что вместе с менеджером состояний вообще не хочет тащить остальную часть экосистемы мола, в которой рекомендуется написание кода на mol tree. Понятное дело, что менеджер состояний отделен от вьюх и всего остального. Может быть это не совсем очевидно.
Ну и можно пример реального приложения в котором "разницы нет"?
Давайте лоб в лоб, вот на таком примере:
https://stackblitz.com/edit/vitejs-vite-js4kjx?file=src%2Fmain.ts&terminal=dev
И то он далеко не из реальной жизни, а завышен многократно, за тик 1000 мутаций, чтений, реакций и все это укладывается всё равно в 3ms у меня, на древнем железе будет 6ms. В реальности делите это на 50-100, а 1% случаев пускай на 10.
А время кадра браузера (60fps) 1 / 60 = 16.6ms, даже в таком нереальном кейсе запас в 13ms. Отсюда и получается что разницы нет.
Сделайте тоже самое на $mol_wire (я не разобрался как, пытался, но ничего не взлетело) и пришлите ссылку на stackblitz.
Этот синтетический тест измеряет какую-то ерунду, бесконечно далёкую от проблем реального приложения.
Этот синтетический тест измеряет какую-то ерунду, бесконечно далёкую от проблем реального приложения.
Не надо опускаться до уровня @artalar
Сделайте пожалуйста, все вместе посмотрим, там реально делов на 5 минут от силы, я бы сам сделал но не разобрался как.
Вот в том же Supercat Store всё понятно, и я тоже сделал с ним вариант:
https://stackblitz.com/edit/vitejs-vite-k6i3qw?file=src%2Fmain.ts&terminal=dev
Он оказался помедленнее MobX, но не критично. @supercat1337
Или вы всё же сделали и результат оказался таким же по скорости или медленнее и вы решили не кидать ссылку? Тогда это вообще не спортивное поведение.
Я вот даже версию с reatom сделал
https://stackblitz.com/edit/vitejs-vite-rnuwpv?file=src%2Fmain.ts&terminal=dev
Да, он побыстрее работает, но платить за это такую цену в виде написания кода какой он предлагает - нет, спасибо. Очень не хватает $mol_wire для сравнения
Супер! Если будут вопросы по реализации той или иной задачи на Supercat Store, без проблем отвечу. Можно и в чате телеги https://t.me/super_cat_dev
Вы измеряете накладные расходы на запись и чтение из одной и той же переменной, когда реактивная система не задействована вообще - это бесполезная информация.
За полезной можете заглянуть сюда: https://mol.hyoo.ru/#!section=docs/=yvd6t1_jkrmkc
Вы измеряете накладные расходы на запись и чтение из одной и той же переменной, когда реактивная система не задействована вообще - это бесполезная информация.
1) Чтение = вызов getter'a, проверка на то надо ли куда подписываться или нет, и если надо подписка. Реактивная система задействована.
2) Запись - вызов setter'a, проверка изменилась ли значение, если изменилось, то вызываем реакции. Реактивная система задействована.
3) На каждой итерации в примере вызывается реакция. Собственно сама реакция в реактивной системе.
В моём примере каждый раз полный цикл реактивности задействован. А вы говорите что он не задействована вообще, смешно. Плюс весь смысл измерить накладные расходы инструмента, а не скорость JS'a, т.к. в функции реакции можно было сделать цикл где 100млн раз дописывался бы символ 'a' к строке, и что бы мы тогда измерили? Правильно, скорость работы этой функции, а не MobX, $mol и т.п.
P.S. Всё понятно, значит вариант с $mol_wire просто медленнее, иначе вы бы не соскакивали с темы. Обидно когда с пеной у рта кричишь что твоя вариации самая быстрая, а в итоге нет.
Никаких подписок не происходит - реактивная система не работает.
Если состояние уже помечено изменённым - ничего не делаем.
Эффективная и корректная система реактивности не вызывает реакции сразу при каждой записи, а как минимум дожидается стабилизации базовых состояний: https://page.hyoo.ru/#!=rim3w4_u79sb
Впрочем, по ссылкам, я смотрю, вы не ходите. А потом недоумеваете, почему с вами перестают разговаривать. Никому не интересно играть в вашу специальную олимпиаду.
Вам про машину говорят, а вы про лопату твердите, что мол черенок у нее не того цвета.
Как только речь зашла о том чтобы код конкретный показать в stackblitz, так сразу в кусты. Отмазки пошли из детского сада и ранней школы. Смешно.
Потом ещё удивляетесь почему никто в здравом уме не рассматривает и никогда не будет рассматривать ничего, что связано с $mol.
Со стороны кажется, что каждый из вас по-своему прав:
В примере есть
autorun()
, использующий значение реактивной переменной. Благодаря тому, что обновления распространяются синхронно на каждой итерации происходит вызов реакции. Т.е. реактивная система действительно задействована.Разные СТМ по-разному кэшируют вычисления, что может сильно повлиять на результаты бенчмарка. Полагаю, речь о том, чтобы сделать дерево зависимостей чуть сложнее (добавив больше компьютедов).
Разные СТМ по-разному кэшируют вычисления, что может сильно повлиять на результаты бенчмарка
Так я специально создал условия чтобы кэширования было невозможным никакое, т.к. на каждой итерации значение действительно меняется и синхронно вызывается реакция.
Мы проверяем в максимально медленном и нереалистичном сценарии, что бы показать, что даже в нем мы с большим запасом укладываемся в стабильные 60fps.
В реальности у меня например сконфигурирован авто батчинг и реакция в этом примере была бы вызвана только 2 раза, в начальный момент и после синхронных изменений. Это всё туда же, к вопросу о том, что якобы mobx может быть источником тормозов.
Ну вот кстати с автобатингом - https://stackblitz.com/edit/vitejs-vite-vslbhn?file=src%2Fmain.ts&terminal=dev
Сразу в 4 раза быстрее на ровном месте.
Полагаю, речь о том, чтобы сделать дерево зависимостей чуть сложнее (добавив больше компьютедов).
При чем тут вообще computed'ы?)) Мы опять про желтое с мягким говорим?))
Но опять же, не вопрос, вот с computed'ом в довесок - https://stackblitz.com/edit/vitejs-vite-m3ixjp?file=src%2Fmain.ts&terminal=dev
Речь идёт про типичный сценарий, computed это вообще частный случай, более того изначально была речь о том, что mobx весь такой медленный, а $mol_wire весь такой быстрый, и что мы имеем в итоге?
Как думаете, теперь мы увидим вариацию с $mol? Правильно, нет. Максимум что мы увидим - новую отмазку.
Вопрос по таблице сравнений (которая где-то вверху): а как у вас так вышло, что на мобиксе нельзя покрыть код линтерами и тестами?
Здравствуйте. На всякий случай повторю тезис из статьи, что эта табличка является примером именно плохого подхода к анализу. Стоит воспринимать ее как "экспонат в музее ошибок".
Конкретно про пункт с линтерами/тестами для MobX - если не ошибаюсь, это о том, что в MobX нет из коробки инструментов типа fork/allSettled. Как сказано в статье - мы тогда на волне хайпа тяготели к тому, чтобы выбрать Effector, поэтому многие критерии оценивали с этой позиции - если нет чего-то, что есть в Effector, то это минус.
Спасибо за статью. Под "в этот раз опирались на более объективные критерии, и выбор был очевидным", как понимаю, имеется в виду MobX? Если так - могу посоветовать пару удобных подходов mobx-stateful-fn и mobx-use-store - они дают очень удобные механизмы. Там же рядом и неплохой роутер, вдруг пригодится)
Спасибо за ссылки. У нас поверх MobX накручена своя архитектура, поэтому многие вопросы уже решены или должны решаться на ее уровне. Но вот асинхронные компьютеды мы как раз обсуждаем, поэтому смотрим на разные варианты решения, в том числе mobx-stateful-fn.
Ребят, это всё хорошо, но вы типа одна из топ корпораций на просторах пост-СССР, у вас огромные ресурсы, есть люди, деньги. Почему вам не сделать мры с предлагаемыми улучшениями или не взять проект под патронаж (раз год с ним возитесь) и выделить хотя бы технического писателя для доки, а не сваливать всё на пару человек мейнтейнеров, которые и так х без соли доедают? Лайк, там литералли стори с донатами на еду есть. Тем более это свои и типа сплочение, я там не знаю, солидарность проявить можно же?
Просто сравнивая даже с мобиксом каким — там авторам по 30к только с донатов прилетает в месяц. Думаю у них из-за этого и мотивация повыше будет ту же проверку на ацикличность делать
Тем более это свои и типа сплочение, я там не знаю, солидарность проявить можно же?
В статье этого нет, но это как раз было одной из важных причин выбора и удержания Effector в нашей кодовой базе - потому что это свои.
Про контрибьют - как описано в статье, в основном нам не хватало функционала, который зачастую в принципе противоречил концепции эффектора (динамическое создание и уничтожение сторов). Поэтому такие костыли остались в нашей кодовой базе.
(дальше может быть не точно)
По поводу финансирования - как у любой топ-корпорации все финансовые вопросы четко планируются. На сколько мне известно, на определенном этапе планирования бюджета на финансирование этот вопрос поднимался, но так вышло что это примерно совпало с нашей "стадией принятия".
Огонь! Ждал подобной статьи с самого первого дня, как познакомился с Эффектором.
Теперь жду чего-то подобного про FSD. Как раз недавно была статья от ВК о начале использования FSD в каком-то проекте :)
Может подскажете, каким образом удалось сделать в ВК замечательный эффект при пролистывании альбома, при котором через каждые 10 фото запросы подвисают от 2 секунд до бесконечности? Что это за маркетинговый ход? К этому же очень интересно, почему прокрутка диалогов настолько забивает память что браузер перестает отвечать, и зачем было заменять рабочий видеоплеер на поделку которая с трудом справляется с задачей просмотра видео. Если все эти доработки мейла направлены для усложнения работы ботом, то есть же API, которое используют для обхода нужного контента. Зачем действовать в ущерб пользователям? Не совсем по теме статьи, но раз вы занимаетесь фронтом, может знаете ответы.
Больше всего меня удивило, что вы не обратили внимание на signal - https://www.npmjs.com/package/@preact/signals-react. У которого даже скачиваний больше, чем у Effector) Он обладает почти всеми "преимуществами" эффектора, которые были упомянуты в этой статье, вдобавок прекрасно себя чувствует при использовании в качестве экземпляра чего-то более крупного. Более того, не знаю как, это чудо умеет перерисовывать ноды напрямую, без перерисовки всего компонента(и его потомков), без запуска методов жизненного цикла, без проверок различных массивов зависимостей прочих хуков. При этом у сигнала есть свой эффект, если нам нужно не только на уровне перерисовки DOM, но и на уровне запуска какого-то кода отреагировать на изменение состояния сигнала. А то всё mobx, mobx)
Cигналы из Preact действительно крутая штука. Я полагаю они не рассматривались по нескольким причинам.
Во-первых, задолго до внедрения эффектора у нас был эксперимент по использованию Preact, но он не взлетел из-за плохой совместимости с некоторыми внутренними решениями, которые уже существовали на тот момент. Полагаю, что этот неудачный эксперимент и то, что сигналы обычно не рассматриваются отдельно от Preact привели к тому, что их не было в табличке сравнения (та же магия обновления нод, о которой Вы упомянули работает только вместе с Preact).
Во-вторых, если сравнивать с MobX, то тут у последнего для нас есть несколько преимуществ:
Объектный стиль получается гораздо лаконичнее - нам не нужно явно писать singnal/computed в подавляющем большинстве случаев, получается обычный JS +минимум бойлерплейта.
MobX все же популярнее и имеет более обширное комьюнити. Мне также не известно крупных проектов, использующих сигналы Preact без самого Preact (тут возможно информация устарела).
та же магия обновления нод, о которой Вы упомянули работает только вместе с Preact
Это неправда) благодаря { useSignals } from "@preact/signals-react/runtime" оно прекрасно работает в реакте
Я не эксперт по сигналам преакта, но кажется что чтобы отделные нативные ноды могли быть реактивными, нужна какая-то логика в самой библиотеке (как, например, у spring есть свои версии нативных элементов) или трансформация при сборке. Решил глубоко не копать, а просто проверить ваше предложение на практике (песочница), и там ожидаемо обновляется весь компонент - можете сами проверить. Я ничего не упускаю?
Он перерисовался, как бы это сказать...без ведома реакта) даже профайлер не покажет перерисовку
к примеру
useEffect(() =>
console.log('render')
);
уже не покажет никаких логов. Более того, вы можете сделать к примеру так
function App() {
useSignals();
const count = useSignal(0);
useEffect(() =>
console.log('render')
);
return (
<>
<SomeComponent1 count={count}/>
<SomeComponent2 />
<button onClick={() => count.value++}>inc</button>
</>
);
}
И при изменении переменной count будет перерисовываться только SomeComponent1. В сравнении с тем же Effector, возможность создавать экземпляры локальных сторов, которые ещё и не аффектят изменения всех дочерних компонентов из коробки - крутая фишка)
function App() {
useSignals();
const count1 = useSignal(0);
const count2 = useSignal(0);
useEffect(() =>
console.log('render')
);
return (
<div>
{ count1.value > 0 && <SomeComponent1 count={count2} onDone={ ()=> count2.value = 0 }/> }
</div>
);
}
Ну а так оба компонента будут ререндериться всегда при изменении count1, хотя в этом нет смысла.
потому что идёт обращение к count.value, которое именно при таком обращении перерисовывает компонент) почитайте доку, если вам нужно текущее значение без триггера, то нужно использовать signal.peek()
Прекрасный совет. Если вам не нравится пуш семантика - выпиливайте реактивность с корнем.
Я наврал, вопрос решается так
const countIsPositive = useComputed(() => count.value > 0);
<div>{countIsPositive.value && <SomeComponent1 count={count} />}</div>
теперь с реактивностью всё в порядке?) такое ощущение, что вы изо всех сил стараетесь найти минусы вместо того, чтобы хотя бы рассмотреть плюсы
https://stackblitz.com/edit/vitejs-vite-a2zysg?file=src%2FApp.tsx&terminal=dev вот, поиграйтесь. Ни одного лога после всех рендеров
Посмотрел - судя по всему сигнал выдает себя за React элемент, что позволяет его срендерить в JSX. Интересная оптимизация, но к сожалению работает только при использовании в качестве контента. Если использовать этот же сигнал в выражении или в качестве свойства - работать не будет =(
return (
<>
<div>{`count = ${count}`}</div>
<div style={{ background: 'red', padding: count.value }}>1</div>
</>
);
В данном примере содержимое просто не обновляется =( Первое еще можно пофиксить с помощью useComputed()
:
const str = useComputed(() => `count = ${count}`);
А вот чтобы обновлялся второй элемент, придется использовать useSignals()
, но в этом случае оптимизация просто перестанет работать (сообщения начнут сыпаться в консоль).
На моем опыте - если библиотека предоставляет несколько вариантов поведения, зачастую команда останавливается на каком-то одном, чтобы не нарваться на неожиданности при рефакторинге. Поэтому, могу предположить, что в реальном проекте принято во всех компонентах использующих сигналы всегда добавлять useSignals()
.
Upd: заглянул в реализацию и оказалось, что для того чтобы "магия" автообновления работала, библиотека патчит React и этот функционал не будет работать в React 19.
Мне нравится концепт сигналов, но такое решение не кажется хорошей идеей.
Он перерисовался, как бы это сказать...без ведома реакта) даже профайлер не покажет перерисовку
Как это без ведома - посмотрите в консоль, там будет вывод "render" на каждый рендер.
Более того, вы можете сделать к примеру так
Ну так в данном случае вы не используете значение в основном компоненте, но дочерние будут рендериться каждый раз при изменении. Тут ничего удивительного. Грубо говоря, любой менеджер состояния так себя поведет, просто в одних за использование считается непосредственное чтение (MobX, Reatom, Recoil и тд), а в других - использование хука (Redux, Effector и тд).
А можете подсказать какие именно бенчмарки использовали для оценки производительности того же redux и других библиотек? О нем есть много мифов, в том числе что нельзя создать несколько сторов (можно), нельзя использовать изменяемое состояние (можно, но подписаться на него нельзя, для этого можно сделать отдельное хэш поле) и тп, много бойлерплейта (с RTK нет), и как раз про производительность.
На практике, в том числе на больших проектах, наблюдал проблемы производительность только один раз, но это было приложение чата на React Native, запущенное исключительно на андроиде, где в едином redux store хранилась даже тема, на которую были подписаны абсолютно все компоненты (facepalm).
Главное преимущество Redux это как раз его максимальная простота (можно на коленке написать за вечер), функциональный стиль (есть мнение, что все что сделано на классах по умолчанию плохо спроектировано, но холивар предлагаю не начинать - на это будет отдельная статья), отсутствие магии и оберток HOC (в функциональных компонентах). Любая проблема, которая когда либо возникала, имеет решение на redux - сюрпризов уже не будет.
Так же рекомендую ознакомиться с моей статьей о react-redux-cache (RRC) - может быть вас это заинтересует.
PS. По поводу effector - многим одного взгляда на его апи и финальный код было достаточно, чтоб не переходить с mobx/redux, и как уже сказано в комментариях многие проблемы были понятны уже давно. Конечно странно что даже в таких огромных компаниях часто следую хайпу, но спасибо что написали эту статью - спорить теперь будет куда легче.
есть мнение, что все что сделано на классах по умолчанию плохо спроектировано
Серьезно?) Это чьё мнение интересно?) И какого перепуга оно должно вообще хоть что-то весить? Тем более зачем именно всё делать на классах? Там где удобно - классы, там где удобно - функции. Опять же классы классам рознь и функции функциям рознь.
отсутствие магии и оберток HOC (в функциональных компонентах). Любая проблема, которая когда либо возникала, имеет решение на redux - сюрпризов уже не будет.
О каких конкретно сюрпризах идёт речь?
Серьезно?) Это чьё мнение интересно?)
Многих людей) Но холивар предлагаю здесь не устраивать. Просто есть такое мнение. Я статью пишу по этому поводу, вас обязательно позову.
О каких конкретно сюрпризах идёт речь?
О тех, что в вк наткнулись, используя очередного хайпового "убийцу redux", и о которых написано в статье.
Здравствуйте. Как написано в статье - большинство критериев оценивалось поверхностно, на основании данных с официальных сайтов библиотек и нашей интуиции. Так, например, авторы эффектора часто заявляют, что их библиотека производительнее, чем Redux и другие конкуренты - вот мы и поверили на слово. Вы сами написали, что вокруг Redux витает много мифов. Эти мифы и стереотипы в нашем случае тоже повлияли на оценку.
По поводу бенчмарков добавлю: достаточно сложно придумать какой-то хороший пример, который бы закрывал все потребности в инструменте и одновременно хорошо подходил для бенчмарканья (чтобы в нем и списки были, и асинхронные действия, и связь между модулями и т.д.). Поэтому во второй раз мы их тоже не делали, а отдельно рассматривали конкретные аспекты работы инструментов, важные для нас, о которых написано в статье.
есть мнение, что все что сделано на классах по умолчанию плохо спроектировано
Присоединюсь к другим комментаторам и с удовольствием послушаю аргументы в пользу этого тезиса. Скиньте ссылку на статью, когда будет готова.
Функциональный стиль не является плюсом - это просто инструмент, такой же как и ООП, иммутабельность, чистые функции и многое другое. Он позволяет за счет определенных ограничений получить конкретные преимущества в конкретной ситуации, но сам по себе не является преимуществом или недостатком.
...отсутствие магии и оберток HOC
А еще подскажите, что в Вашем понимании "магия"? И почему она и HOC'и это плохо?
Функциональный стиль не является плюсом - это просто инструмент, такой же как и ООП
В современных языках типа Go и фреймворках типа React (да это фреймворк) от ООП отказались не просто так. И создатель Java признавал что зря добавил классы. Вот про это скоро напишу статью и Вас тоже приглашу.
отсутствие магии и оберток HOC
Чем проще инструмент, тем меньше с ним проблем. Например в Redux максимально простая архитектура - неизменяемое дерево состояния, и все на него подписаны через React.Context (Provider) - нет проблем с очередностью подписок, c циклическими зависимостями и мн. др.
Соответственно чем сложнее и непонятнее инструмент - тем больше потенциальных подводных камней может возникнуть. Магия это как раз совершенно неочевидные большинству разработчиков, непривычные вещи типа makeAutoObservable и пр.
К сожалению не все разработчики чувствуют простоту архитектуры, и пресловутую "магию" - и часто ведутся на хайп, как в вашем случае. Хотя это главная задача архитектора - сделать систему настолько простой, насколько это возможно.
По поводу HOC - они 1) увеличивают vdom - по сути вместо одного слоя становится два (x2), что ухудшает производительность и отладку, стектрейс 2) усложняет типизацию - со всеми этими обертками часто сложно "прокинуть" нужный тип компоненты если он generic 3) увеличивает вложенность - компоненту можно обернуть в 5 HOC, либо в ней вызвать 5 hook подряд - во втором случае код будет куда более читаемый. Во первых видна последовательность вызовов, и можно легкочитаемо прокинуть данные из одного hook в другой. Тогда как в первом варианте придется добавлять пропы возвращаемым компонентам непонятно кому что.
В современных языках типа Go и фреймворках типа React (да это фреймворк) от ООП отказались не просто так. И создатель Java признавал что зря добавил классы. Вот про это скоро напишу статью и Вас тоже приглашу.
А, то есть вот такого рода "логика" и "аргументы", ну тогда ясно понятно. [Facepalm]
фреймворках типа React (да это фреймворк)

React - библиотека для web и нативных(мобильные приложения) пользовательских интерфейсов.
Чем проще инструмент, тем меньше с ним проблем
Карбюратор - простой инструмент для ДВС. Казалось бы, с ним не должно быть проблем или совсем чуть чуть.
Инжектор - сложный инструмент для ДВС и кучей датчиков, и электроники. Казалось бы, с ним должно быть полно проблем и надежность никакая.
Реальность - инжектор уделывает карбюратор в щепки по всем параметрам. В том числе по кол-ву проблем.
Вот ваша логика либо черное либо белое и рассыпалась.
По поводу HOC - они 1) увеличивают vdom - по сути вместо одного слоя становится два (x2), что ухудшает производительность
Сколько это в цифрах? Десяток микросекунд? И это на полный цикл рендера всей страницы. А не перерендера компонента в отдельности.
усложняет типизацию - со всеми этими обертками часто сложно "прокинуть" нужный тип компоненты если он generic
Да ладно? observer в mobx-react-lite типизируется 1 в 1 так же как голый реакт компонент.
React - библиотека
Разрабам хочется верить что это библиотека тк фреймворки это "не круто", но по сути это фреймворк, ибо все приложение строится вокруг него. Бизнес логика обновлений запускается при монтировании компонент, бизнес логика приложения запускается при монтировании главной компоненты (обычно App) и тп. Кто то может делать по другому, но смысла в этом нет никакого. Даже подписаться на изменение чего нибудь не через React.Context ведет к багам. Выкинуть React из приложения и заменить его на другую "библиотеку" зачастую равносильно полному переписыванию.
Карбюратор - простой инструмент для ДВС
Карбюратор обладает куда более худшими ползовательскими характеристиками чем инжектор, а у нас речь про технологии, выполняющие абсолютно одно и то же. Пользователь не заметит разницы в использовании сайта что на redux, что на mobx, если они написаны с одними требованиями. Вопрос лишь в сложности реализации, поддержки и тп.
Вы приводите нелогичные примеры.
Сколько это в цифрах?
Если оборачивать каждую компоненту, vdom увеличиться в 2 раза. На огромных сайтах, запущенных на слабых [андроид] девайсах с севшей батарейкой может быть заметно. Я бы в такую историю не вписывался если есть выбор. Точные цифры можете замерить сами.
На остальные пункты я смотрю ответов не нашлось)
observer в mobx-react-lite..
Я в целом про HOC vs hook, на вашем mobx свет клином не сошелся. Хотя и там могут быть проблемы, проверять нет желания.
Даже подписаться на изменение чего нибудь не через React.Context ведет к багам
Что за фантазии? Вы вообще знаете в чём заключается суть React.Context?
Выкинуть React из приложения и заменить его на другую "библиотеку" зачастую равносильно полному переписыванию.
Его и нет смысла выкидывать если использовать по назначению - только как view слой. А управление состоянием как локальным так и глобальным доверить гораздо более удачным решениям, например MobX.
Карбюратор обладает куда более худшими ползовательскими характеристиками чем инжектор, а у нас речь про технологии, выполняющие абсолютно одно и то же.
А карбюратор и инжектор это не технологии для приготовления топливо воздушной смеси?))
Они не выполняют абсолютно одно и то же(топливо воздушная смесь)?)))
Пользователь не заметит разницы в использовании сайта что на redux, что на mobx, если они написаны с одними требованиями. Вопрос лишь в сложности реализации, поддержки и тп.
Это факт, так как как и не заметит разницы обернуты ли компоненты в HOC'и или нет. И да, действительно сложность реализации при использовании MobX куда ниже.
Если оборачивать каждую компоненту, vdom увеличиться в 2 раза. На огромных сайтах, запущенных на слабых [андроид] девайсах с севшей батарейкой может быть заметно.
Проверено многократно, не заметно, от слова совсем.
Я бы в такую историю не вписывался если есть выбор.
Есть такая штука - компромисс, да можно жертвовать нано и микросекундами в угоду значительному удобству и скорости написания кода.
на вашем mobx свет клином не сошелся. Хотя и там могут быть проблемы, проверять нет желания.
Логично, вы ничего не проверяете, просто берете утверждения из воздуха или где-то что-то прочитали/услышали и принимаете это за истину.
В современных языках типа Go и фреймворках типа React (да это фреймворк) от ООП отказались не просто так.
Насколько мне известно, в Go не отказались от OOP. Там есть структуры и множество способов выстраивать отношения между ними. Наследование - не единственный признак ООП;
В данном случае вы апеллируете к авторитету. С тем же успехом можно сказать, что "в современных языках типа Rust и фреймворках типа Angular от ООП не отказались не просто так";
И создатель Java признавал что зря добавил классы
Тем не менее одна из самых популярных платформ и большинство инструментов и библиотек в ней используют именно ОО подход и паттерны.
Я не говорю, что ООП лучше ФП, но и заявлять обратное тоже считаю некорректным. У каждого подхода свои плюсы/минусы и область применения.
Магия это как раз совершенно неочевидные большинству разработчиков, непривычные вещи типа makeAutoObservable и пр.
Любая более-менее сложная технология неотличима от магии. Если не вдаваться в принципы работы используемого инструмента, то даже самый простой может работать как магия и подкидывать сюрпризы где их не ждешь. Пара примеров:
Выше в комментариях мы обсуждали сигналы, и там тоже есть "магия" обновления нативных нод, которая при незнании может обернуться тем, что компонент начнет обновляться частично.
Тот же контекст, который Вы упоминаете, при неправильном использовании приводит к большому количеству лишних обновлений UI. Если попробуете погуглить, то можете даже обнаружить что многие люди в принципе считают контекст плохой практикой, "потому что он приводит к обновлению всего и вся" (на самом деле это не так).
По поводу HOC - они
Интересно, но когда React был на классах - HOC'и были крутым паттерном переиспользования логики, но как-то со временем все извратилось и сейчас то же самое говорят про хуки, хотя на самом деле:
Хуки - совершенно чуждая любому здравомыслящему разработчику концепция, просто создатели React не придумали ничего лучше для функциональных компонентов. Попробуйте вспомнить использование подобного подхода где-то еще.
Хуки позволяют переиспользовать логику ровно в той же мере, что и обычные утилитарные функции. Вот только любой компонент на хуках намертво к ним прибит, в то время как логика HOC полностью отделена от логики компонента и они оба могут быть использованы независимо.
Используя хуки
useEffect()
,useCallback()
иuseMemo()
вы порождаете новую функцию (и замыкание, которое кстати дороже класса) на каждый рендер, даже если она в итоге не будет использована. Это дополнительно нагружает GC. Лично я не стану заявлять, что лишние статичные ноды в React-дереве бьют по производительности сильнее.Более того, проверка зависимостей этими хуками не бесплатна (как и внутренняя логика их вызова в React) и происходит каждый рендер. Именно поэтому использование
useMemo()
иuseCallback()
получается таким противоречивым - вроде по логике нужно заворачивать в них все, что потом попадает в пропы дочерних компонентов, а вроде неизвестно - мемоизированы ли дочерние компоненты и будет ли это иметь смысл?Использование хуков увеличивает функцию рендера, задача которой преобразовать входные данные и состояние в UI. Вместо этого в той же функции мы описываем инициализацию, эффекты и деструктуризацию. Очень странно слышать, что функция перегруженная ответственностью читается легче, чем несколько отдельных самостоятельных функций.
Если Вы прокидываете данные между HOC'ами, вы скорее всего делаете что-то неправильно (например, разделяете то, что не должно быть разделено). То, что Вам приходится передавать значения между хуками - это как раз следствие того, что на этот костыль наложили слишком много ответственности. Например,
useMemo()
иuseCallback()
можно заменить простымmemoize-one
, который кстати можно вызывать в условиях и циклах.
Но это все абстрактно. Если говорить предметно, про тот же MobX - там существует один единственный HOC, у которого отсутствуют описанные Васм проблемы и его использование позволяет отказаться от большей части хуков (особенно от самых неудачных), сделав код сильно более читаемым.
в Go не отказались от OOP... Наследование - не единственный признак ООП
Вы ошибаетесь. Именно классы - по сути раннее связывание данных и методов (не интерфейса и методов), и наследованные - и есть ООП. И в Go ни классов, ни наследования нет. Оставили ООПшный стиль вызова функций через точку, чтоб совсем джавистов не распугать.
вы апеллируете к авторитету
Скорей даю подсказку что можно покопать самому в данном направлении, так как уже сказал что пишу длинную статью на эту тему и нет смысла ее писать здесь в комментариях. А язык Go как раз и создавали, чтобы уйти от проблем ООП языков, и успешно с этим справились - то есть над этим точно задумывались. Как и тех кто делал React - эти сами прошли по всем граблям, и каждый сегодня может сам сравнить ООП vs функции. Думаю никто в здравом уме сегодня не выберет классы как основной способ создания компонент.
Тем не менее одна из самых популярных платформ и большинство инструментов и библиотек в ней используют именно ОО подход и паттерны.
А в музыкальных трендах вашей соцсети откровенное 💩, и?) Мне бросить Pink Floyd, Кино, ZZ Top и мн. др. и пойти слушать нашу эстраду?
Я не говорю, что ООП лучше ФП, но и заявлять обратное тоже считаю некорректным
Вы руководствуетесь в суждениях хайпом, как мы уже видели, хайп сегодня относительно ООП и ФП, если не особо разбираться - "каждый инструмент хорош" (раньше кстати был что ООП это лучшая парадигма, предсказываю что скоро будет что ООП - отстой). Я стараюсь руководствоваться логикой. А по логике для каждой конкретной задачи есть только одно лучшее решение. Дальше можно набрать список задач, в которых ООП будет лучше процедур / функций, и тут можно уже начать спорить. Раннее связывание данных/функций и наследование по моему мнению хуже всегда. И я об этом поспорю с удовольствием в комментариях к статье.
Любая более-менее сложная технология неотличима от магии
Чем лучше технология, тем она проще, понятнее, и в ней меньше "магии" (KISS). И это основной показатель качества инженерии.
Хуки - совершенно чуждая концепция.
Когда то и классы были чуждой концепцией. Хотя и до сих пор - разрабы React моментально пересели на функции с хуками, и возвращаться даже не думают. Хотя я согласен - можно было сделать то же самое и "без магии". Забавно, что здесь "магию" вы уже не защищаете.
Хуки позволяют переиспользовать логику ровно в той же мере, что и обычные утилитарные функции
Нет, они могут добавлять состояние и эффекты.
любой компонент на хуках намертво к ним прибит, в то время как логика HOC полностью отделена от логики компонента
Очень странно слышать, что функция перегруженная ответственностью читается легче, чем несколько отдельных самостоятельных функций
Да, и это пожалуй единственное преимущество HOC. Вот только излишнее "отделение" логик без должной необходимости есть оверинжиниринг. В абсолютном большинстве случаев лучше вызвать 10 хуков подряд в одном компоненте, передавая туда нужные данные, чем обернуть компоненту в 10 HOCов, прокидывающи друг другу пропы - не позавидовал бы тем, кому пришлось бы работать с таким кодом.
Используя хуки
useEffect()
,useCallback()
иuseMemo()
вы порождаете новую функцию. Это дополнительно нагружает GC.
Да, все эти излишние создания объектов можно было бы избежать, сделай они чуть менее сахарной реализацию хуков. Но учитывая что каждый рендер и так создает дофига объектов - элементов (а создание функции +/- = создание объекта), то можно допустить, что вряд ли это хоть как то сильно сказывается на производительности относительно постоянного пересоздания vdom. Тут уже реально замерять надо. А GC кстати может работать в свободное от рендеринга время, и даже параллельно.
Именно поэтому использование
useMemo()
иuseCallback()
получается таким противоречивым - вроде по логике нужно заворачивать в них все
С этим гемором useCallback уже научились жить более менее, но они скоро обещают добавить useEvent.
useMemo()
иuseCallback()
можно заменить простымmemoize-one
В функциональных компонентах нельзя. А в классах useCallback заменяется стрелочными функциями у this. useMemo да, в классах использовали его. И?
Если Вы прокидываете данные между HOC'ами, вы скорее всего делаете что-то неправильно. То, что Вам приходится передавать значения между хуками - это как раз следствие того, что на этот костыль наложили слишком много ответственности.
Да что вы говорите. Тот же connect у redux наверное очень плохой, как же его переписать чтоб ничего не прокидывал? контекст создавать? А есть например forwardRef из React. А значения между хуками перекидываются банальными useMemo и useCallback - они ведь как принимают, так и возвращают значения.
Если говорить предметно, про тот же MobX - там существует один единственный HOC, у которого отсутствуют описанные Васм проблемы и его использование позволяет отказаться от большей части хуков (особенно от самых неудачных), сделав код сильно более читаемым.
Я так понимаю тут речь про хранение локального состояния и функций в mobx - вот тут с удовольствием бы поспорил предметно, с примерами. В том числе рассмотрел бы возможность сделать тоже самое на основе обычного useRef без лишних библиотек.
Вы ошибаетесь. Именно классы - по сути раннее связывание данных и методов (не интерфейса и методов), и наследованные - и есть ООП.
Даже в самом названии ООП нет слова "классы", речь об объектах. А наследование - лишь один из принципов. Можем хоть к Википедии обратиться. Ну или кидайте свои более правильные ссылки)
Скорей даю подсказку
Да не, апеллируете к авторитету =)
А язык Go как раз и создавали, чтобы уйти от проблем ООП языков, и успешно с этим справились - то есть над этим точно задумывались
Откуда информация? У Вас в предложении отдельные части не согласуются.
Как и тех кто делал React - эти сами прошли по всем граблям, и каждый сегодня может сам сравнить ООП vs функции
А разве на функции перешли не потому, что они лучше ложились конкретно на концепцию файберов (это не точно, ссылку искать лень).
Думаю никто в здравом уме сегодня не выберет классы как основной способ создания компонент.
Еще раз напомню, что кроме React есть другие инструменты, например Angular, в котором на классах все прекрасно организовано.
А в музыкальных трендах вашей соцсети откровенное 💩, и?) Мне бросить Pink Floyd, Кино, ZZ Top и мн. др. и пойти слушать нашу эстраду?
Субъективное суждение.
Не понимаю, как оно относится к моему тезису про то, что Java прекрасно живет с ООП и никто там даже не пытается от него убежать. Выглядит как эмоциональный выброс.
Вы руководствуетесь в суждениях хайпом
предсказываю что скоро будет что ООП - отстой
Выше в другом комментарии я писал, что мне кажется что текущая ФЕ повестка как раз давно уже звучит как "ООП - отстой". Лично я каждый раз спрашиваю того, кто так заявляет в чем причина такого мнения. Еще ни разу не получил конкретного аргумента. Ждем Вашу статью.
Чем лучше технология, тем она проще, понятнее, и в ней меньше "магии" (KISS)
Вы немного не так поняли мой тезис. Я имел в виду, что понятие "магия" как будто зависит от того, кто его применяет. Я специально привел Вам примеры случаев, когда люди могут не вдаваться в подробности устройства тех или иных техник, и тогда они превращаются для них в магию. Стоит в этом разобраться (даже поверхностно) - и вся "магия" уступает место логичному и предсказуемому поведению.
Когда то и классы были чуждой концепцией.
Новой, но не чуждой. Мой тезис был про то, что хуки - априори не новый подход, а костыль.
разрабы React моментально пересели на функции с хуками, и возвращаться даже не думают.
Вы тут имеете в виду непосредственно разработчиков библиотеки, или тех кто ей пользуется? Мотивы первых мне не известны, а вторые думаю ясно почему "моментально пересели".
Кстати, Вы же знаете, что функции до сих пор не могут полностью заменить классы?
Забавно, что здесь "магию" вы уже не защищаете.
Это я не понял, про что Вы. На всякий случай поясню - я не за/против магии, я лишь говорю, что "магия" = "я не понимаю как оно там работает" и этот принцип для разных людей работает на разных масштабах. Как Event Loop - кто-то знает как он работает в деталях, а кому-то это не интересно.
Нет, они могут добавлять состояние и эффекты.
Вы не до конца прочитали этот пункт:
В официальной документации есть раздел про переиспользование логики за счет выделения ее в кастомные хуки. И это то, что многие разработчики считают плюсом.
При этом - их можно использовать в нескольких разных компонентах, но компоненты будут прибиты к этим хукам намертво. В то время как компонент можно завернуть в разных HOC'и без вмешательства в его нутро.
Да что вы говорите. Тот же connect у redux наверное очень плохой, как же его переписать чтоб ничего не прокидывал
О чем тут речь? Что Вы там прокидываете из одного HOC'а в другой?
Я так понимаю тут речь про хранение локального состояния и функций в mobx - вот тут с удовольствием бы поспорил предметно, с примерами.
Как локально состояние, так и глобальное. А вот и пример
https://stackblitz.com/edit/vitejs-vite-ffchmx?file=src%2Fpages%2Fmain%2Findex.tsx&terminal=dev
В том числе рассмотрел бы возможность сделать тоже самое на основе обычного useRef без лишних библиотек.
Сделайте тоже самое, что пунктом выше и пришлите ссылку
Для локального состояния и функций - кладете их все в useRef, при изменении состояния вызываете forceRender. Можно завернуть в один удобный хук. Все.
Про глобальное я не говорил.
cc @markelov69
Это не ответ. Это просто отписка.
Вот:
https://stackblitz.com/edit/vitejs-vite-ffchmx?file=src%2Fpages%2Fmain%2Findex.tsx&terminal=dev
Тоже самое от вас должно быть, только с useRef как вы и говорили. Нужно видеть конкретный код, который в точности повторяет этот функционал.
Без ссылки на stackblitz это всё просто слова.
Я говорил про локальное состояние, отвечая на "отказаться от большей части хуков", понимая это как от useState, useCallback и useMemo. Хотя не вижу в этом большого смысла.
А в вашем примере используется глобальное состояние, и я бы 100% для этого использовал redux + react-redux-cache, redux (RTK) куда лучше mobx (хаха).
Больше повторять одно и то же не буду.
То есть опять пустые слова и кода не будет да?
Отмазки опять какие-то начались. Вам дали конкретный код. Повторите его так, как вы считаете правильным, чтобы полностью такой же функционал был. А мы просто сравним итоговый код.
А в вашем примере используется глобальное состояние, и я бы 100% для этого использовал redux + react-redux-cache, redux (RTK) куда лучше mobx
Так используйте, покажите код который полностью повторяет функционал. Кто ж против. В этом и весь смысл.
Больше повторять одно и то же не буду.
Классический слив, классического пустослова. Вообще максимально ожидаемо.
А есть например forwardRef из React.
Штож, никто и не говорит, что все решения принятые командой React идеальные.
Я так понимаю тут речь про хранение локального состояния и функций в mobx.
Тут речь о том, что проблем с HOC'ами в MobX нет. Конкретно относительно Ваших тезисов хотя бы потому, что MobX не предлагает создавать HOC'и, он лишь предоставляет один для превращения обычных компонентов в реактивные. Это как memo()
в React, который делает компонент чистым.
Но технически - да, можно и локальным состоянием прекрасно управлять без большей части хуков. Получается очень приятный такой MVVM. На всякий случай уточню, что речь про бизнес логику.
Вот тут с удовольствием бы поспорил предметно, с примерами.
Выше по комментариям были разные примеры кода на MobX. Если их не достаточно - придумайте задачку и возможно мы с Вами сравним решения.
Кстати, по поводу HOC'ов - они не обязательно обязаны создавать дополнительную ноду в React дереве)
Чем лучше технология, тем она проще, понятнее, и в ней меньше "магии" (KISS). И это основной показатель качества инженерии.
Инжектор vs Карбюратор ?)
АКПП с гидротрансформатором vs МКПП ?)
Голый Javascript vs React + JSX ?)
Ноги vs Велосипед ?)
Автомобиль vs Велосипед ?)
И этот список можно продолжать до бесконечности, где ваше "правило" не выполняется. Оно в каких-то конкретных случаях выполняется и я с ним полностью согласен, а в каких-то конкретных случаях оно не выполняется.
Оно выполняется всегда, просто у вас проблемы с пониманием.
Например ноги или велосипед - вы видимо считаете что велосипед всегда лучше, но это зависит от требований. Например если речь идет о передвижении по небольшому многоэтажному торговому центру, либо поднятии на 5 этаж без лифта, то однозначно ноги - так как соответствует требованиям и куда проще.
Оно выполняется всегда, просто у вас проблемы с пониманием.
Да? Интересно.
Инжектор vs Карбюратор ?)
АКПП с гидротрансформатором vs МКПП ?)
Голый Javascript vs React + JSX ?)
Ноги vs Велосипед ?)
Автомобиль vs Велосипед ?)
А почему карбюратор мягко говоря не всегда лучше? Или только у идиотов инжекторные машины?
А почему МКПП не всегда лучше? Или только у идиотов АКПП?
А почему ноги не всегда лучше велосипеда? Или на них только идиоты ездят?
Ведь вы говорите
Оно выполняется всегда
"Чем проще, тем лучше"
А я говорю что не всегда. Где-то да, а где-то нет.
Например ноги или велосипед - вы видимо считаете что велосипед всегда лучше, но это зависит от требований. Например если речь идет о передвижении по небольшому многоэтажному торговому центру, либо поднятии на 5 этаж без лифта, то однозначно ноги - так как соответствует требованиям и куда проще.
Да? Я считаю что всегда лучше велик?) А если нужно преодолеть расстояние в 30км? В 60км? Что лучше? Всё ещё ноги?
Я же писал:
Оно в каких-то конкретных случаях выполняется и я с ним полностью согласен, а в каких-то конкретных случаях оно не выполняется.
Можно вы читать не умеете?) И сами это подтвердили в вашем примере с ногами vs велик.
Еще раз - смотрим требования и находим решения что одинаково устраивают владельца продукта. Выбираем из них самое простое.
В примере с преодолением расстояния 30км, после озвучивания вариантов (пешком, на велосипеде, автомобиле, истребителе или ракете "Союз") будет принято решение, что устраивают только варианты от велосипеда и выше, а пешком не устраивает, значит пешком не подходит под требования, и самым простым и дешевым решением будет велосипед.
Из велосипедов, где педали нужно крутить ногами, либо руками при езде вверх головой, выбор - ногами (и то зависит от требований, может это цирк).
Если же нужно тренировать выносливость у срочников, то лучшее решение - бегом.
В примере СТМ - redux и mobx выполняют абсолютно одну и ту же функцию. Это даже не бегом / на велосипеде, это скорей на велосипеде и педали крутим ногами или руками вниз головой. И только в цирке выберут второе.
У вас явно проблемы с пониманием. Больше не трачу время.
А язык Go как раз и создавали, чтобы уйти от проблем ООП языков
Интересно девки пляшут

Ни слова про "проблемы" ООП или уход от ООП.

Да, сразу видно они решили вырубить всё на корню что связано с ООП.
Даже в языке СИ(~1972г.) в с помощью struct
можно делать подобием класса/объекта
struct student
{
int name;
int address;
void (*speak)();
};
void speak_func() {/*...*/}
int main()
{
//code ..
struct student student;
student.speak = speak_func;
student.speak();
}
И как раз таки можно создать функцию обертку которая будет сразу возвращать экземпляр student и связанные с ним методы. Проверено ещё лет 7 назад когда писал на СИ.
Классы это всегда было и будет удобным способам организации код, только там где это действительно удобно. Ты обращаешься к экземпляру класса/объекта и тебе IDE сразу подсказывает все его свойства и методы, ну разве это не прекрасно?
Я стараюсь руководствоваться логикой
Вы апеллируете к "авторитетам". А это значит полное и тотальной отсутствие реальной логики, здравого смысла и критического мышления. Отсутствие этих качеств - крайне плохо влияет на способность быть талантливым разработчиком.
ООП — это про обмен сообщениями, внутренний стейт объектов и позднее связывание. Почитайте или посмотрите про Алана Кея — человека, который придумал ООП.
Самый ООПшный язык — это вообще Erlang, но в нем нет ни классов, ни наследования, даже нет переменных в привычном понимании.
А по логике для каждой конкретной задачи есть только одно лучшее решение.
Не согласен. Сложные задачи имеют более одного правильного решения, оптимальность которых зависит от применяемых критериев оценки (по скорости, по размеру, по потреблению памяти, ...).
Особенности Effector, которые почему-то никто не обсуждает: опыт ВКонтакте спустя год использования