Как стать автором
Обновить

Комментарии 47

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

Если у вас такая система управления состоянием, которая всё сливает в некий глобальный скоуп (да, это недвусмысленный пинок redux) — то стоит подумать о том, а точно ли вам нужна именно эта система управления состоянием?

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

Оснащение проекта поддержкой TypeScript <...> может быть отложено до того момента, когда приложение окажется подверженным ошибкам типизации

Тогда будет уже поздно. Основная «соль» статических типов не в том, что они от определенных ошибок страхуют (хотя вы это получите бонусом), а в том, что статические типы позволяют явно ответственно относиться к структурам данных. А безответственное отношение получит отлуп либо от компилятора, либо от коллег на PR (потому что структуры данных явно видны).
Проекты, в которые TS был добавлен уже сильно потом — изобилуют прекрасными сигнатурами в духе
function doStuff(spec: ISpec | IOtherStuff | string ): string | boolean | null 

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

PS: пример выше был взят из реального проекта, кстати, с заменой имен на абстрактные.
Typescript + React + MobX с самого начала и будет вам счастье и масштабирование до бесконечности без головной боли. Причем React надо использовать тупо как View слой, т.е. не использовать его встроенные возможности по управлению состоянием.
Тогда разработка будет доставлять удовольствие, а не боль, страдание и разочарование и ярое желание каждый день переписать всё с нуля или свалить на другой проект, где не было допущено фатальных ошибок в закладывании архитектуры.

А вы не рассматривали mobx-state-tree?
Она кажется очень наглядной по сравнению с разрозненными мини-сторами mobx.
Как вы решаете связность сторов mobx?

А вы не рассматривали mobx-state-tree?

mobx-state-tree уничтожает всю суть и мощь MobX'a, так что категорически нет.
Она кажется очень наглядной по сравнению с разрозненными мини-сторами mobx.
Как вы решаете связность сторов mobx?

А что там решать то? Просто обычная работа с объектами и классами.
Можете описать конкретный кейс, который доставляет проблемы/вопросы, а я напишу код для него.
mobx-state-tree уничтожает всю суть и мощь MobX'a, так что категорически нет

То есть фреймворк от создателя Mobx, предоставляющий структуру уничтожает суть и мощь? Может быть вы просто не разобрались в mobx/mobx-state-tree?

То есть фреймворк от создателя Mobx, предоставляющий структуру уничтожает суть и мощь?

И что? Теперь поклонятся любому его «творению» что ли? Пусть пишет всё что угодно, мне то что. Я головой думаю, а не смотрю, кто именно написал ту или иную штуку. Если штука реально крутая, то я использую, иначе не использую. Вы посмотрите исходники, как этот автор код пишет, там кровь из глаз пойдет. Но именно MobX штука крутая, а то как он написан в плане кода, это ужас.
Может быть вы просто не разобрались в mobx/mobx-state-tree?

Смешная шутка.
А что там решать то? Просто обычная работа с объектами и классами.

Наверняка вы используете какие-то дополнительные пакеты из мира mobx.
Может быть это mobx-router или mobx-cache или же mobx-persist?

Наверняка вы используете какие-то дополнительные пакеты из мира mobx.
Может быть это mobx-router или mobx-cache или же mobx-persist?

Нет, от слова совсем.
Только mobx и mobx-react(-lite).

А роутер я давным давно написал сам, чтобы был понастоящему реактивный, так же с использованием mobx'a (есть стейт currentRoute в котором есть вся инфа о роуте и параметрах из урла), т.к. готовые решения меня полностью не устраивают, увы они ущербны.
mobx-state-tree уничтожает всю суть и мощь MobX'a

Ого, какой интересная точка зрения!
Не могли бы вы развернуть, почему mst уничтожает всю суть и мощь mobx?

А вы не рассматривали mobx-state-tree?

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

Как вы решаете связность сторов mobx?

Я до сих пор не понимаю, какая людям нужна связность кроме «стор А получил* инстанс стора Б». И зачем им для этого городить сложности.

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

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


Можно создать и описать такие слои и структуру самому, сделать еще один велосипед. А можно воспользоваться готовым решением, с готовой документацией.

Можно создать и описать такие слои и структуру самому, сделать еще один велосипед.

Учитывая, что связность тех или иных частей проекта так или иначе зависит от домена (иначе говоря, от предметной области) — то и структура должна быть частной. Когда мне говорят (mobx-state-tree), что весь стейт надо сложить в дерево моделей просто потому, что «так правильно» — я предлагаю пойти лесом. Когда мне в ответ добавляют «но у вас же будет time travel из коробки!» — я отвечаю, что time travel я реализую сам в конкретных точках приложения, там, где он необходим, а в других точках он мне не нужен.

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

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

Реальность такова, что «обычные» разработчики в рамках строжайше навязанной архитектуры тоже спокойнейше нафигачат неподдерживаемый код, который потом кому-то более способному придётся переписать. Неважно, что вы там берете — mobx-state-tree, redux, или другое. Опция «написать фигово» присутствует абсолютно всегда, и довольно слабо зависит от того, сколько там препонов на этом богоугодном пути выставлено архитектурой, компилятором, или еще чем.
В довольно сложном приложении возникает необходимость в создании специальных слоев приложения, ответственных за определенные вещи. А также в создании определенной иерархии. Иначе проекты превращаются в кашу из различных подходов.

Можно создать и описать такие слои и структуру самому, сделать еще один велосипед. А можно воспользоваться готовым решением, с готовой документацией.


Приведите прямо конкретный случай пожалуйста и то, как вы это решаете с MST (код на codesandbox.io), а я вам решу вашу же задачу с использованием голого MobX'a, может и JustDont тоже подключится и свой вариант решения тоже напишет, а вы посмотрите на все это и сделаете выводы. И мы тоже, вдруг ваш вариант с MST будет лучше чем наш.

Без конкретных примеров, вы говорите просто теорию, при который нубас разработчик напишет говнокод, если приложение будет сложнее Todo List'a, а нормальных разработчиков типо не существует и они по определению пишут говно и говяные велосипеды, а вот если готовое решение использовать, это это уже тру подход.
а нормальных разработчиков типо не существует и они по определению пишут говно и говяные велосипеды, а вот если готовое решение использовать, это это уже тру подход.

К сожалению, такова реальность. Можно написать SSR самому, а можно взять nextjs, который диктует рамки.


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


Я рад, что такая проблема не стоит перед вашим выбором)

К сожалению, такова реальность. Можно написать SSR самому, а можно взять nextjs, который диктует рамки.

Мой опыт разработки говорит о том, что чем строже будет структура проекта, тем чище и поддерживаемей окажется код.

Вместе с этим вы загоняете в рамки и себя самого, сами топчитесь на месте и не развиваетесь. Становитесь заложником той или иной фигни и вынуждены терпеть тяготы и ограничения воинской службы.
Котелок должен постоянно думать и придумывать решения для сложностей с которыми сталкиваетесь, иначе он деградирует и с каждым днем всё сложнее и сложнее впитывает в себя новую информацию и теряет способность думать полноценно.
А то получается шаг влево и шаг в право, так всё, ступор, идет поиск новых готовых решений и т.п., всё что угодно лишь бы самому не думать и не принимать решения и не строить архитектуру (а то вдруг говно создам, засмеют ведь, проще все списывать и сваливать на библиотеки/фреймворки и то, как они диктуют тебе писать код, типо это не я пишу говно код, так просто принято писать в моем фреймворке/биболиотеке, их же не дураки писали, а гуру).
Не говоря уже о том, что сколько бы рамок вы не наставили, код от этого писать лучше никто не станет и говно оттуда никуда волшебным образом не денется. Есть простая закономерность — чем больше человек в команде, тем больше говна в проекте, если человек один, то и говна там не будет, если он конечно сильный разраб.

Чуть пропустишь код-ревью, и получится неподдерживаемая запутанная непроизводительная жуть.

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

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


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

Eslint, типы Typescript, Prettier, настройка husky, Storybook и регрессионное тестирование.


Как это говно никуда не денется? Его станет меньше. Куда меньше. Чем больше таких рамок, тем лучше бизнесу. Быстрее фичи, меньше тех. долга, меньше рефакторинга.


P.S.:
Из вашего сообщения вообще складывается ощущение, что настоящий продакшен вы не писали, раз "Взять и написать свой SSR" это лучше, чем взять "next.js", ведь это — загон в рамки себя самого.

Из вашего сообщения вообще складывается ощущение, что настоящий продакшен вы не писали

:D ага) да и вообще я только 2ой месяц изучаю программирование)
раз «Взять и написать свой SSR» это лучше, чем взять «next.js», ведь это — загон в рамки себя самого.

И в чем тут проблема собственно?) Как это связано с продакшеном?)
Next.js не взял — в продакшен ни разу не писал и вообще дэбил? =)

Eslint, типы Typescript, Prettier, настройка husky, Storybook и регрессионное тестирование.

Как это говно никуда не денется? Его станет меньше. Куда меньше. Чем больше таких рамок, тем лучше бизнесу. Быстрее фичи, меньше тех. долга, меньше рефакторинга.

Каким образом это коррелирует с говнокодом? Отступы пофиксились, импорты отсортировали, типы написали всё, говна нет?) Говнокод это очень обширная тема, а не такая поверхностная.
То что вы описали, помогает для борьбы только с верхушкой айсбрега. Да, эти инструменты нужны, но они просто вспомогательные, не надо строить иллюзий. Остальные 90% процентов никуда не исчезают, с ними все так же надо бороться рефакторингами и PR'ами.

Заранее извиняюсь, что не по теме, но обращаюсь ко всем участникам спора: что изучать человеку, который хочет нормально научится во фронтенд, а не пополнить ряды 90%? Желательно со ссылками. Просто уже на этапе изучения введения в React с доработкой игры в крестики-нолики почувствовал, что вечные setState и переписывание доброй половины классов на каждую доработку не вдохновляют от слова совсем и почитав понял, что реализация логики в самом React — путь в никуда.
P.S. опыт в разработке есть, но в JS и вебе я нуб.
React нужно использовать только как view слой, управление состояниям там отвратительное. MobX использовать для управления состоянием как локальным у компонентов, так и глобальным. Плюс Typescript для типизации. И тогда будет реальное удовольствие от разработки, а не вечная борьба с самим собой и с технологиями.
Ну и разумеется перед этим надо хорошенько изучить JS. А не быть «Framework developer'ом» =)

Вот этой ссылки для быстрого ознакомления с React + MobX тебе будет более чем достаточно на первое время
codesandbox.io/s/suspicious-water-5wwnz
Тут и локальное состояние и глобальное, а ещё асинхронные вызовы (эмуляция без реальных запросов к серверу)
Спасибо за и ответ и за ссылку. Код без комментов — наше все, я сам такой же ;)
P.S. в каком смысле «для типизации» — чтобы наплодить классов для storege-й?
Код без комментов — наше все, я сам такой же ;)

Ну там на самом деле простой код и он «Self explained».
P.S. в каком смысле «для типизации» — чтобы наплодить классов для storege-й?

Классы для storage-й НЕ связаны с типизацией)
Типизация это вот www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAZxAWwBQEMBcizoBGApgE4A0iheBaJpAlDUWYgN4BQi3ipxUIUkiyIA1FQDcHAL4cOqTACIsyACaLKAJgYTEAej2IA7qQQBzOTDBQywLBGKIAkgFVkrTj3xY0xPMihSKzMpLywzP3wWUilZDggAGxVkRDcyZABlKDhScMdPHhB3UmQ8V2KAbQBdRABeRGrdRBk5CAQAxCL0srTSarqGrh4Cr24wH0jFcd9EAEYNIdHEPLxZxcRpcnWR0enJvcRNBaXuFcP12SqpePaoTuLM7NyIgbBiI1SHrJy8jB0OLolb7PYgAOkBKXqEKkEOBeXBDwGFVm5E05AAzFd9IYTOYgA
Так смысл возвращаться к статической типизации… время на разработку-то увеличивается, а решения как правило не стоит высекать в камне, но за информацию о возможностях — спасибо
время на разработку-то увеличивается

Препочитаете потратить столько же и больше времени на юнит-тесты вида «аргумент N должен быть строкой, а не строкой быть не должен»? Или же на дебаг из-за того, что вы таки где-то в вашем коде куда-то пихнули данные не того вида?
Это если такая ситуация выплывет до изменения требований
P.S. впрочем у меня нет опыта именно во фронтенде, может я ошибаюсь
Еще раз спасибо за пример
Ну там на самом деле простой код и он «Self explained»
Действительно Self explained (когда прочитаешь описание всех вещей который в нем использованы) только вот почему observer используется вместе с useState (в компоненте App)?..
useState который в качестве аргумента принимает функцию выступает в роли конструктора класса и функция которая ему передана как аргумент выполнится только 1 раз, за все время жизни компонента и результат который вернет это функция как раз и будет 0вым элементом в возвращаемом массиве
const [ state ] = useState(() => new State());

тоже самое по сути что
constructor(){
    this.state = new State();
}
О, ясно (сорри что поздно увидел ответ), работает даже
var state = new LocalState();
получается, useState — чистый синтаксический сахар?

P. S. То, что useState выполняет функцию 1 раз а не хранит её и отдаёт как значение по-умолчанию понял ещё по поведению (иначе она бы историю-то не хранил), но вот почему useState ведет себя именно так для меня загадка… Возможно особенность JS которую все ещё не чувствую

Возможно я уже надоел, но не могу не спросить — как собственно выше приведенный пример нормально запустить как одностраничное приложение для отладки? Или как обзерверы и хуки можно нормально отлаживать?
Просто в вебаппе который сейчас клепаю не хотят работать собственно обзерверы — и вопрос на самом деле более глобальный — как их вообще отлаживать? В WebStorm видно что они не работают, но почему не видно ни разу. Появилась идея сравнить с помощью React Developer Tools с эталоном, но эталон-то в webstorm запускаться отказывается
Support for the experimental syntax 'decorators-legacy' isn't currently enabled
Решения навроде тыц не помогают.
Может есть онлайн сандбокс который может сформировать полноценную вебстраницу для отладки или другой общественно одобряемый способ отладки реакта?
Что значит не работают? Именно ваш код не работает, а не обзерверы.
Во первых:
Реактивность в плане изменения работает только когда вы изменяете свойство объекта так
obj.value= 123;

// или
obj.texts = ['1', '2', '3'];
obj.texts[1] = 'asd';

вот так не работает
const { value } = obj;
value = 123;

// или (так тоже не работает)
obj.texts = ['1', '2', '3'];
const row1 = obj.texts[1];
row1 = 'asd';

Это ограничение языка.

Во вторых:
У вас там используется MobX 6 версии и mobx-react-lite 7 версии, они вышли вот только что и я бы не рекомендовал их использовать пока, они не стабильные скорее всего.
Используйте MobX 5.х и mobx-react-lite 6.x

Во третьих:
У вас там input'ы без обработки onChange, значит попытки ввода в них текста работать не будут

В четвертых:
— Я делаю "....", ожидаю что должно произойти "....", а происходит "....".
Задайте вопрос таким образом и вы получите на него ответ.

Вопросы в стиле «Ничего не работает» не канают.
Вот за такой развернутый ответ спасибо!
Вопрос был собственно о средствах отладки, что кто-то будет разбирать конкретные ошибки и не рассчитывал.
P.S. А для чего в Вашем примере onChange устроен так сложно?
const handleTitleChange = useCallback((e) => {
    state.handleTitlechange(e);
    globalState.globalTitle = e.target.value;
  }, []);
ведь работает
const handleTitleChange = (e) => {
    state.handleTitlechange(e);
    globalState.globalTitle = e.target.value;
  }
и даже
const handleTitleChange = (e) => {
    state.title = e.target.value;
    globalState.globalTitle = e.target.value;
  };
P.P.S. знаю что тут memoized version, но она же все равно каждый раз вызывается и сразу, так как отрисовать изменения нужно сразу
Eslint, типы Typescript, Prettier, настройка husky, Storybook и регрессионное тестирование.

Это всё не мешает писать говнокод. Это помогает замечать говнокод, и это помогает нечаянно не писать говнокод. Но если у вас нет никого, кто бы сознательно боролся с этим на code review, и есть «обычные» программисты, которым главное, чтоб компилятор промолчал, а не чтоб код был хорошим — то сколько инструментов не привлекай, а результат всё равно будет так себе.
Eslint, типы Typescript, Prettier, настройка husky, Storybook и регрессионное тестирование.

Как это говно никуда не денется? Его станет меньше. Куда меньше. Чем больше таких рамок, тем лучше бизнесу. Быстрее фичи, меньше тех. долга, меньше рефакторинга.

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

Это вы про один стор чтоли? Но в mst же можно делать несколько сторов?

Нет, это я про навязывание определенного конкретного вида организации данных no matter what.

Глобал стор — это даже не худший аспект редакса, это просто откровенное недоразумение.

в смысле, конкретного вида организации данных?
там же такая же организация данных, как и в mobx, просто обёрнутая типизацией…


в чём вы наблюдаете отличие?

Серьезно? А чего меня документация встречает словами «Central in MST (mobx-state-tree) is the concept of a living tree.»? И дальше полностью исходит из того, что я прям побежал укладывать свои структуры в верхнеуровневую древовидную организацию, при том, что мне это запросто (в зависимости от решаемой задачи) может вообще нафиг не впиваться?

просто обёрнутая типизацией

У меня и так в MobX в тайпскрипте всё «обёрнуто типизацией». А RTTI мне ни разу не пригодился на фронте. Для валидации данных на внешнем периметре RTTI не требуется (можно всё нужное сделать в compile time), а кроме этого я вообще не представляю, зачем мне в рантайме знать, что some.model.property у меня типа стринг.

living tree вовсе не заставляет всё переделывать в верхнеуровневую древовидную организацию. Тут скорее ударение на living надо ставить, а не на tree


А большинство моделей — они и так сами по себе представляют собой иерархическую структуру.


RTTI там по большей части нужен для сериализации / десериализации и разворачивании всяких штук типа reference, а валидация там — скорее ну, дополнительная штука.


В зависимости от решаемой задачи) может вообще нафиг не впиваться?

с этим тезисом я вполне соглаашусь, но то, что mst не подошёл для вашей задачи, не делает же его "просто откровенныс недоразумением"


Мне вот mst весьма зашёл в тех местах, где понадобилось иметь сериализуемые / десериализуемые модели (для отправки / загрузки на сервер). Хотя и базовый mobx я тоже использую (но уже в других местах)

living tree вовсе не заставляет всё переделывать в верхнеуровневую древовидную организацию

Не заставляет, да. MST все же не редакс какой, и гибкость там имеется. Но всё равно, зачем оно?

А большинство моделей — они и так сами по себе представляют собой иерархическую структуру.

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

RTTI там по большей части нужен для сериализации / десериализации

Сериализацию/десериализацию можно делать столькими (в целом очень похожими) способами, что наверное даже в лонгрид на хабре их описание не уместить. И RTTI тут *опять же* не обязателен (потому что тот же подход что и с валидацией, если в compile time нагенерировать по типам весь нужный код, то далее в рантайме типы не требуются), хотя конечно в определенном разрезе упрощает описание решения.

Мне вот mst весьма зашёл в тех местах, где понадобилось иметь сериализуемые / десериализуемые модели (для отправки / загрузки на сервер).

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

Не спорю. Поэтому я посмотрел, прикинул, и понял, что mst неплохо так ложится вот это конретную мою задачу.


и вообще-то есть либы именно для сериализации, а не MST ради этого брать.

Ну, да, есть, но есть и mst, я же там не только сериализацию использую, но и computed-values, actions и прочие mobx-совские штуки + в плане неоходимость добавлять undo/redo для операций, так почему бы мне не заюзать mst — удобно же


Можно ли было это сделать иначе — да разумеется, куча способов. но поскольку mobx я уже использовал, то решил в одном попробовать mst и оно зашло.

Везде — только index.js

Можно настроить в VS Code:
"workbench.editor.labelFormat": "short"
Зарегистрируйтесь на Хабре, чтобы оставить комментарий