Comments 32
Что касается MobX, то я слышал о нем много хорошего, но изучить как следует пока не успел.
Меня вот всегда удивляло, почему люди тратят кучу времени на изучение всякой ерунды и написания больших статей о ней, а до чего-то более-менее практичного руки так и не доходят.
Что касается MobX, то я слышал о нем много хорошего, но изучить как следует пока не успел.
Серьезно? То есть тратить время на Redux, Recoil, Redux-Toolkit и т.п. это да, а 30 минут потратить на изучение MobX который на 2 порядка лучше их всех вместе взятых это нет?
Ну ладно, у меня для вас хорошие новости, вы можете изучить его буквально за 15 минут.
codesandbox.io/s/nameless-platform-ynxrm?file=/src/App.tsx
И вот вам TODO list набросал быстренько
codesandbox.io/s/wizardly-cori-pyorp?file=/src/App.tsx
Вы там потеряли работу с сетью и отображения спиннеров. Асинхронщина у мобикса всё же является слабым местом. Так что ваш приветмир не очень показателен.
enforceActions: "never"
Чем вам экшены-то помешали?
Чем вам экшены-то помешали?
Код загрязняют, не люблю грязный код и ненужные нагромождения. На моей обширной практике связки react + mobx ни разу не было замечено даже намека на тормоза в реакт приложениях с 2016 года и по сей день.
Асинхронщина у мобикса всё же является слабым местом
Нет. С чего вдруг? С того что там нет автобатчинга который при реальной необходимости решается заворачиваем в runInAction?
Вы там потеряли работу с сетью
Нет, у меня есть эмуляция запроса к АПИ который ждет 2 секунды. Разницы нет вообще ни какой.
Так что ваш приветмир не очень показателен
Более чем, чтобы понять как работает MobX и начать с ним работать прямо сейчас.
Нет. С чего вдруг? С того что там нет автобатчинга который при реальной необходимости решается заворачиваем в runInAction?
Там нет поддержки Suspense API, а значит приходится писать интерактивную лапшу без автотрекинга зависимостей в асинхронном коде.
у меня есть эмуляция запроса к АПИ который ждет 2 секунды.
У вас может и есть, но по ссылке на пример туду аппа, что вы привели, ничего этого нет.
Более чем, чтобы понять как работает MobX и начать с ним работать прямо сейчас.
Ну вот завернёт человек асинхронную функцию в @computed и будет удивляться чего это ничего не работает.
Там нет поддержки Suspense API, а значит приходится писать интерактивную лапшу без автотрекинга зависимостей в асинхронном коде
Да не нужно писать никакой лапши, ведь приложение не тормозит из-за этого, если вдруг будут реальные тормоза из-за этого, чего в real world приложения ни разу не наблюдал, то в конкретном месте можно завернуть в runInAction, делов то. Я не спорю что MobX можно было сделать ещё по лучше под капотом, но как обычно: если хочешь сделать хорошо — сделай сам.
У вас может и есть, но по ссылке на пример туду аппа, что вы привели, ничего этого нет.
Я скинул 2 ссылки, там абсолютное разное все. Посмотрите их 2 и вы увидите.
Ну вот завернёт человек асинхронную функцию в @computed и будет удивляться чего это ничего не работает.
Не завернет, прежде чем он вообще узнает что есть computed, он посмотрит его документацию, потратит 30 секунд и поймет как он работает и для чего нужен и главное как именно должен применяться.
не нужно писать никакой лапши, ведь приложение не тормозит
Тормоза тут ни при чём. Речь про лаконичность кода и его багоёмкость. Интерактивный код более багоёмкий из-за поценциального состояния гонки. Реактивный же код лишён этой проблемы, но мобикс не умеет в саспенс. Как-то не тянут ваши слова на "обширную практику".
хочешь сделать хорошо — сделай сам
Либо воспользоваться (внезапно!) альтернативами, а не форсить везде мобикс, словно на нём свет клином сошёлся.
Посмотрите их 2 и вы увидите.
В первой ссылке вообще какой-то синтетический пример. Реальные приложения так не работают. Бессмысленный код — такая себе иллюстрация.
Не завернет, прежде чем он вообще узнает что есть computed, он посмотрит его документацию
Я просто процитирую ваши же слова:
Более чем, чтобы понять как работает MobX и начать с ним работать прямо сейчас.
Тормоза тут ни при чём. Речь про лаконичность кода и его багоёмкость. Интерактивный код более багоёмкий из-за поценциального состояния гонки. Реактивный же код лишён этой проблемы, но мобикс не умеет в саспенс. Как-то не тянут ваши слова на «обширную практику».
Вместо пустых слов напишите код в codesandbox где MobX реально обсирается из-за того, что не умеет в саспенс. С удовольствием посмотрим.
поценциального состояния гонки
Не является конкретной проблемой MobX'a и т.п., это является потенциальной проблемой любого асинхронного кода, но решается элементарно.
Вот:
codesandbox.io/s/quizzical-sid-x2znn?file=/src/index.ts
Вот именно, что "решается". А с более другими технологиями подобные проблемы вообще не возникают и их решать не приходится. Чувствуете разницу?
Покажите хоть одну технологию, где проблемы асинхронного кода решать не приходится.
Нагромождения await не предлагать.
Ну вот простой пример: https://codepen.io/nin-jin/pen/yLVZVpq?editors=0010
Ну вот простой пример: codepen.io/nin-jin/pen/yLVZVpq?editors=0010
Это вообще не то и не про то. Вы знаете что такое гонка вообще? Это то, что я кидал выше, вот вы например на кнопочку жмете много раз и там происходят асинхронные вызовы, но вас интересует только то, что после самого последнего вызова должно происходить, т.к. остальные уже не актуальные.
У вас же один раз только делается запрос и всё, остальное это просто картинки вывести.
Вот такой код у себя попробуйте, разумеется он нифига не спасает от гонки, так что вы несете очередной бред, а я же вам показал реальное решение проблемы
const sleep = (ms: number) =>
new Promise((resolve) => {
setTimeout(resolve, ms);
});
const get_json = $mol_fiber_sync(async () => {
await sleep(1000);
console.log('Race condition survivor');
return 123;
});
class App {
@$mol_mem
static emojis() {
return Object.values(get_json("https://api.github.com/emojis"));
}
@$mol_mem
static presenting() {
document.body.innerHTML = "";
const btn = document.createElement("button");
btn.innerText = "Click me!";
btn.addEventListener("click", get_json);
document.body.appendChild(btn);
}
}
App.presenting();
Вы мой пример выше посмотрите, потыкайте кнопочку много раз, посмотрите как должно это работать, а потом уже че-то говорите и пишите, а то нифига не смотрите и лепите какую-то чушь не имеющую отношения к делу.
Обратите внимание, что в моём коде нигде нет установки всяких isFetching
на этапе загрузки данных, которые являются типичными источниками гонки. Это не потому, что я про них забыл, а потому, что они не нужны.
Я поправил ваш код: https://codepen.io/nin-jin/pen/rNWPwVX?editors=0011
При этом он не просто проигнорирует ненужные запросы, а коррректно их отменит.
Если что — кнопка форк внизу справа.
В моем варианте все решается только 2мя строчками:
const stillActual = noRaceCondition(handleClick);
// async calls...
if (!stillActual()) return;
При этом он не просто проигнорирует ненужные запросы, а коррректно их отменит.
Вау, очуметь, вот прям киллер фича. Вы же понимаете что в реальной жизни это не нужно от слова совсем. Игнора хватает за глаза.
Обратите внимание, что в моём коде нигде нет установки всяких isFetchingна этапе загрузки данных, которые являются типичными источниками гонки. Это не потому, что я про них забыл, а потому, что они не нужны.
Увы и ах, они нужны. Показ спинеров и любая проверка текущего состояния грузим/не грузим ещё как нужна.
А вообще если для вас проблема писать 2 строчки кода, то вы можете написать no-race-conditins-loader какой нибудь для webpack'a, который будет в функции помеченные декоратором @no_race_conditions вставлять эти строчки в нужных местах, вот это уже будет реально нормальная тема.
слишком много оберток абсолютно всего в $mol_* и в целом слишком много кода
В прикладном коде там только декораторы к методом добавить. Всё остальное — прячется в библиотеки.
Про то, что чтобы понимать что там происходит и реально самому начать пользоваться вашим $mol'ом надо убить просто кучу времени я вообще молчу.
Да не больше, чем на изучение хуков Реакта и мобыкса.
В моем варианте все решается только 2мя строчками:
Так у меня вообще одной:
@autocancel
Вы же понимаете что в реальной жизни это не нужно от слова совсем. Игнора хватает за глаза.
В реальной жизни и игнор этот ваш не нужен совсем. Собственно, поэтому в $mol этого autocancel ещё и нет, ибо никому не был нужен. А корректная отмена запросов очень даже нужна, если важна производительность и не хочется, чтобы браузер тратил время на обработку ответа, который никому не интересен, а сервер продолжал транзакцию, которую можно было бы отменить.
Увы и ах, они нужны. Показ спинеров и любая проверка текущего состояния грузим/не грузим ещё как нужна.
В $mol спиннеры сами (реактивно) показываются, их не надо интерактивно руками ставить и убирать.
А как ваше решение отработает когда порядок респонсов от сервера будет не такой как порядок запросов? Есть предположение, что ни один обработчик не сработает
А как ваше решение отработает когда порядок респонсов от сервера будет не такой как порядок запросов? Есть предположение, что ни один обработчик не сработает
Оно отработает абсолютно корректно и ожидаемо, порядок респонсов может быть любой. Плюс вы можете легко это проверить собственноручно, а то вдруг я вам тут втираю дичь и обманываю.
const counter = rcMap.get(functionLink) || 0;
const currentCounter = counter + 1;
rcMap.set(functionLink, currentCounter);
Вот это выполняется в момент вызова функции, которая в последствии отправляет асинхронные запросы, а не после, поэтому на порядок респонсов вообще не влияет.
return () => {
return rcMap.get(functionLink) === currentCounter;
};
Будет гарантированно возвращать true только на самый последний реальный вызов.
Серьезно? То есть тратить время на Redux, Recoil, Redux-Toolkit и т.п. это да, а 30 минут потратить на изучение MobX который на 2 порядка лучше их всех вместе взятых это нет?
Как человек в свое время изучавший MobX «за 30 минут, еще и на ходу» для применения в реальной задаче («планировщик связанных задач») могу ответственно заявить:
— MobX действительно простой и понятный
— Кода действительно в разы меньше чем у того же Redux
Но…
— Если ты плохо знаешь MobX(*) и тащишь его в задачу сложнее «типичной тудушки» то в результате можно получить прекрасное спагетти где черт ногу сломит понимать что где мутирует и на что влияет. Потому что «магия».
Подобный «нубский» код на Redux будет стремным, копипастным, распухшим, но более очевидным. Потому что «будьте добры однозначно описать ваши намерения».
Но моя основная позиция относительно «Стейт-менеджмента» до сих пор неизменна и сводится к классической «аксиоме Эскобара». Потому что «золотого Грааля» в данном вопросе еще не изобрели, увы.
* Несомненно этот пробел можно «подлатать» фундаментальными знаниями по архитектуре, но… у скольки «вкатывающихся в IT» (особенно с каких-нибудь курсов «React с 0 до 100 тыс в месяц») они есть? Я и сам года 3 назад считал что это «слишком энтерпрайзно», сейчас вот начитался умных книжек и поумнел (правда все что я могу сейчас сделать с этими знаниями это фейспалмить глядя на то как очередной «компонент-многостаночник» нежно размазывает генеренные GraphQL типы по шаблону...)
Но…
— Если ты плохо знаешь MobX(*) и тащишь его в задачу сложнее «типичной тудушки»
2 часа проб и экспериментов и ты знаешь его отлично! Поэтому эта «проблема» не актуальна. Да и магии там никакой нет, просто getters/setters (опять же 15-30 минут про них почитать и поэкспериментировать и ты всё знаешь)
черт ногу сломит понимать что где мутирует и на что влияет
Вообще элементарно
WebStorm и VS Code умеют сто лет в обед — «Find References / Find Usages» где сразу видно в каких местах переменная читается, а в каких местах переменная изменяется.
Хех, сначала упарываются по динамической типизации, а потом жалуются, что не могут найти где мутирует и на что влияет.
2 часа проб и экспериментов и ты знаешь его отлично! Поэтому эта «проблема» не актуальна.
Мы говорим о чуть-чуть разных «знаниях», но к этому вернемся позже.
Да и магии там никакой нет, просто getters/setters (опять же 15-30 минут про них почитать и поэкспериментировать и ты всё знаешь)
Немного странно топить за MobX и не знать что с 5.х версии getters/setters заменили на Proxy, ну да ладно. В случае же «магии» я имел ввиду вроде как общеупотребимое в IT-среде понятие когда разработчик берет «А» кидает его в «черный ящик» библиотеки и получает «Б» и при этом большую часть работы по взаимодействию «А» и «Б» происходит в том самом «черном ящике».
На очень грубом псевдо-JS это как-то так:
@observable A;
@computed B () { return A + 1 }
A = 4;
console.log(B); // "5" потому что заботливый MobX где-то "за кадром" протянул ниточку от A к B
Вообще элементарно
WebStorm и VS Code умеют сто лет в обед — «Find References / Find Usages» где сразу видно в каких местах переменная читается, а в каких местах переменная изменяется.
Когда при поиске причинно-следственных связей приходится раз 5 попрыгать между 2-4мя файлами это уже не совсем «элементарно». А если нужно computed вывести из пары-тройки других? А если эти другие тоже «нетривиальны»? Необходимость рисовать на бумажке граф связей observable/computed как-то далековато от понятия «элементарности».
Да, это проблема плохого OOD, а не MobX.
Но MobX, благодаря своей простоте и магии, не запрещает абстрактному джуну сварить сочное спагетти из пары-тройки (а то и больше) сторов нежно связанных двумя-тремя десятками observable/computed и вдобавок политое сверху соусом из пятка container-components (которые тоже не прочь пощупать сторы через свои ui-events). И в итоге уже через годик этот же джун (или его коллега) «элементарно» устанет прыгать по «Go to Definition» в поисках наиболее оптимального пути внедрения новых фич.
И я напомню что ситуацию про спагетти из сторов я взял не из воздуха, я лично написал такую фигню и лично вот ее разгребаю
TL;DR: Проблема MobX не в самой библиотеке, а в той свободе которую она предоставляет. И тут нужно понимать что для какого-нибудь топ-синьора эта самая свобода — благо, дающее возможность построить именно ту систему которую нужно. В то же время как не каждый мидл способен без посторонней помощи совладать со счастьем писать «что-нибудь и как-нибудь» и все работает.
TL;DR #2: Чтобы написать сложный, но хороший и понятный, код на MobX недостаточно просто знать MobX, полезно еще знать всякие OOD, SOLID и компанию.
Проблема MobX не в самой библиотеке, а в той свободе которую она предоставляет.
Это не проблема, проблема только в кривых ручках разработчика. Вот и все.
И я напомню что ситуацию про спагетти из сторов я взял не из воздуха, я лично написал такую фигню и лично вот ее разгребаю
Это лишь говорит что-то о вас, а не о MobX. Ведь вы же пишете сами фигню, как вы говорите.
Немного странно топить за MobX и не знать что с 5.х версии getters/setters заменили на Proxy, ну да ладно.
Да что вы говорите? Серьезно прям getters/setters не используются? Смешно однако. Вы похоже кроме слова Proxy ничего не видели и не слышали, про самому с этим поиграться я уже вообще молчу.
user = new Proxy(user, {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error("Отказано в доступе");
} else {
let value = target[prop];
return (typeof value === 'function') ? value.bind(target) : value; // (*)
}
},
set(target, prop, val) { // перехватываем запись свойства
if (prop.startsWith('_')) {
throw new Error("Отказано в доступе");
} else {
target[prop] = val;
return true;
}
}
});
Почитайте, чтобы в следующий раз не сесть в лужу.
learn.javascript.ru/proxy
P.S. Джуны и мидлы пишут говнокод всегда и точка. Библиотека/фреймворк и ЯП роли не играют. И никакой архитектуры даже близко они не состряпают. На то они джуны и мидлы. Опыт и стиль мышления решает.
Какой смысл приводить какие-то «аргументы» опираясь на них?
Это не проблема, проблема только в кривых ручках разработчика. Вот и все.
Я не пытался подвергать сомнению прямоту лично ваших рук. Но я прожил уже достаточно чтобы убедиться что даже «лучшие из лучших» умудряются ошибаться (можно сходить на ютуб и поискать записи live-coding чтобы лично в этом убедиться).
Это лишь говорит что-то о вас, а не о MobX. Ведь вы же пишете сами фигню, как вы говорите.
Только я в своем сообщении 3 раза уточнил в чем именно «проблема», а вы почему-то решили что обсуждение личных качеств разработчиков это интереснее (ваше P.S).
Да что вы говорите? Серьезно прям getters/setters не используются?
Тут скорее произошло недопонимание, т.к под getters/setters я почему-то вспомнил только старый добрый Object.defineProperty (который к слову никуда не делся даже из нового MobX). Но я предпочту посидеть в тихой и спокойной луже потому что мы переходим к финальному аккорду.
P.S. Джуны и мидлы пишут говнокод всегда и точка. Библиотека/фреймворк и ЯП роли не играют. И никакой архитектуры даже близко они не состряпают. На то они джуны и мидлы. Опыт и стиль мышления решает.
Какой смысл приводить какие-то «аргументы» опираясь на них?
Наверное потому что мы живем все таки в реальном мире?
В мире где в большинстве своем именно джуны и мидлы своим «говнокодом» фиксят баги и пилят фичи разной степени сложности (пока «синьоры» в комментариях решают чья либа круче =) ).
А еще в этом мире лычка «синьор» тоже такое себе мерило «опыта и стиля мышления» (мне же не нужно рассказывать о чудесном мире аутсорса/аутстаффа?).
Поэтому в нашей суровой реальности прилежный мидл вполне может, решая простейшую задачу по «перегонке json в список в браузере», получить в итоге красивую мини-архитектуру где «запросы в API отдельно, состояние списка отдельно, и сама вьюха списка поделена на красивые компонентики». А потом запушить это в общую репу, где похожие «синьорские» компоненты напрямую fetch-ат из componentDidMount…
Впрочем это все лирика и дальнейший «спор» нецелесообразен. Вы нашли свой «золотой mobx-грааль» и как-то слишком фанатично отстаиваете его непогрешимость. Ваше право, но мне неохота быть объектом его реализации.
А остальные после ознакомления с этой веткой сами смогут сделать свои выводы, чай не маленькие.
Поэтому в нашей суровой реальности прилежный мидл вполне может, решая простейшую задачу по «перегонке json в список в браузере», получить в итоге красивую мини-архитектуру где «запросы в API отдельно, состояние списка отдельно, и сама вьюха списка поделена на красивые компонентики». А потом запушить это в общую репу, где похожие «синьорские» компоненты напрямую fetch-ат из componentDidMount…
Примечательно, что в $mol эта ситуация не возможна в принципе. А всё потому, что там самый простой путь — правильный. А в том же Реакте самый простой путь — не правильный. Это и замыкания, приводящие к лишним ререндерам, и ручной запуск запросов в интерактивном стиле — типичный источник множества проблем, и ручная реконциляция пропсов, которая нужна для эффективности, но все ленятся это делать, пока петух не клюнет.
В случае же «магии» я имел ввиду вроде как общеупотребимое в IT-среде понятие когда разработчик берет «А» кидает его в «черный ящик» библиотеки и получает «Б» и при этом большую часть работы по взаимодействию «А» и «Б» происходит в том самом «черном ящике».
В Реакте с этим его виртуальным домом всякой "магии" на порядок больше — "верстаем html, а оно само потом обновляется!". А вебпак — что творит вебпак! Собирает, преобразовывает, свг-шки конвертит. Но почему-то о переизбытке магии говорят только относительно МобХ.
Что же относительно ваших примеров то за всех говорить не могу, лишь свои мысли:
— React — в современном виде (хуки, саспенс и прочие навороты) стал не менее «магичен». Но, основная его концепция (то самое «меняем стейт — верстка обновляется») все еще остается достаточно «приземленной магией», потому что разработчик все еще должен объяснять «глупому» Реакту, на его «реактовом» языке как меняется стейт и какая из этого должна получиться верстка.
В то же время MobX одной строчкой в конструкторе makeAutoObservable(this) превращает простой и тупой js-класс в свой вариант Observable, и в то же время для разработчика это все еще продолжает выглядеть как «простой и тупой js-класс» (особенно когда это прилетает из сторонней либы). А потом мы оборачиваем тупой React-компонент магическим словом observer, прокидываем в него «тупой js-класс» и теперь мы можем напрямую теребить объект js-класса (и может быть даже вообще в другом месте!), а компонент сам будет на это реагировать.
Мы написали всего-лишь 2 вызова, и класс превратился в «состояние», а компонент начал это состояние отслеживать. Чтобы провернуть подобный трюк на чистом React понадобится больше 2х «магических слов».
Потому mobx и «магия».
— Webpack — весьма странный пример в контексте обсуждения библиотек/фреймворков, это все-таки инструмент помогающий в разработке, но в целом на нее не влияющий. Да и вообще «голый webpack» (по крайней мере версий 3, 4) все на что способен это «взять много js-файлов — выдать один js-файл». Вся остальная «магия» прячется в лоадерах и плагинах и требует добротного полотнища webpack.config чтобы заработать. Вот если бы вы упомянули parcel, тогда да «чистейшая магия».
И я напомню что ситуацию про спагетти из сторов я взял не из воздуха, я лично написал такую фигню и лично вот ее разгребаю
Думаю и на каком-нибудь редаксе вы бы написали точно такую же лапшу, только на сагах/санках. Причина говнокода не в свободе. Причина говнокода обычно в кривой архитектуре, где нельзя сделать хорошо и прямо одновременно, и поэтому приходится идти окольными путями.
Но, вот этот твит, заставляет задуматься =).
Одним из преимуществ деструктуризации массива, в отличие от деструктуризации объекта, является возможность использования произвольных названий переменных
У объекта тоже можно. В который раз убеждаюсь, что об этом немногие знают :)
const { propertyName: customName } = someObject;
React: основные подходы к управлению состоянием