Комментарии 40
Redux — это детище создателей ядра React.
Ден Абрамов написал Redux ещё до присоединения к команде Реакта, если не ошибаюсь (по крайней мере, он не был сотрудником FB)
Лично я обожаю MobX. Но, к сожалению, «отраслевым стандартом» считается Redux.MobX потихоньку тоже становится стандартом.
Но я не встречал ни одного готового шаблона админки или фреймворка с Mobx(
Если какие-то готовые компоненты используют менеджер состояний, то это почти всегда redux(
Еще recoil появился. Из-за того, что он от разрабов фейсбука, у него большой шанс стать стандартом в будущем.
Плюс в современных примерах а-ля «есть context и хуки, наконец-то можем проще, без redux» по-прежнему по привычке тянут эти редьюсеры, диспетчеры. Эх, испортил Ден фронтенд основательно и надолго(
Самый большой фейл многих разработчиков которые пишут на React'e — не знание того, что есть MobX и то, что Redux давно должен лежать на помойке.
Связка React + MobX делает из React'a отдельную и главное реактивную технологию, уже совершенно взрослую и реально мощную. Без боли, без страданий и без тонн лютого и не поддерживаемого говнокода (который считается нормой в react+redux апликухах).
Мне кажется, что автор Redux пытался изобразить https://www.martinfowler.com/eaaDev/EventSourcing.html на джаваскрипте. Получилось10 строчек кода и тут он обрёл просветление. Я не вижу в этом ни чего плохого, кроме попытки оставить комьюнити один на один с низкоуровневым API на несколько лет. А потом они родили redux-toolkit, и этим стало можно пользоваться без слёз.
redux-toolkit, и этим стало можно пользоваться без слёз.
Все еще со слезами на глазах, но явно лучше, чем ничего =)
У них добавлены многие штуки из коробки, но все еще не очень понятно, что делать с nested данными, особенно, если у вложенных данных нет уникальных id. А вложенность трехуровневая. (Реальный кейс, где redux-toolkit не справляется без шаманства).
С Mobx/Mobx-state-tree шаманить практически не нужно.
Описанный подход создаёт жёсткую связь от подчинённого компонента к владеющему компоненту. Нормально в обратном направлении — владелец вызывает метод подчинённого, причём для этого не нужен какой-то глобальный объект, ведь владелец знает своих подчинённых, если же подчинённый лежит в каком-то компоненте-обёртке, то на нём (компоненте-обёртке) создаётся проксирующий метод. В описанном же варианте периодически будут возникать ситуации, когда казалось бы универсальный компонент перемещается в другое место с полным удалением бывшего владельца и от этого ломается. Правильный подход — использование всплывающих событий — подчинённый компонент просто эмиттит событие о том, что с ним случилось, если какому-то предку вверх по иерархии нужно как-то на это реагировать, он подписывается на это событие. Куда бы не перемещался подчинённый компонент, он не ломается, тк. связь не жёсткая. Если два компонента никак не вложены друг в друга и всё равно должны взаимодействовать, то взаимодействие делается через общего для обоих предка, который подписывается на событие одного компонента и вызывает метод другого.
если же подчинённый лежит в каком-то компоненте-обёртке, то на нём (компоненте-обёртке) создаётся проксирующий метод и компонент-владелец использует егоНа фронте вложенность часто бывает глубокой. Проксирование через десятки компонентов вниз по иерархии становится проблемой, от которой и пытаются уйти, используя для взаимодействия какую-нибудь штуку снаружи всей этой иерархии, расстояние до которой не зависит от вложенности.
Проксирование через десятки компонентов вниз
у меня такое проксирование даже через две обёртки случалось довольно редко, хотя приложения делал очень не маленькие. В любом случае даже через глобальный объект можно общаться событиями не создавая жёсткой связи.
UPD: глобальным объектом в этом случае обычно делается корневой компонент приложения который виден из всех компонентов как this.ownerComponent
(или this.rootComponent
).
В чем преимущество компонента как глобального объекта по сравнению с контекстом?
Для передачи вниз контекст прекрасно подойдёт, а вот для передачи вверх без создания жёсткой связи он мне кажется малопригоден.
UPD: хотя я тут подумал, почему нет? В каком то верхнем компоненте создать свойство контекста с экземпляром EventEmitter-а, который будет прекрасно виден, в тч. в соседних друг относительно друга компонентах. Так что, наверно, ничем не хуже.
ИМХО меняете шило на мыло как по мне. Как минимум, вы могли бы иметь index.js файл для компонентов, в котором бы экспортился законнекченный к стору компонент, и соответственно когда вам нужна связь с хранилищем вы юзаете его, когда нет — используете напрямую Component.js. Своим подходом вы создаёте жёсткую связь (что является антипаттерном в программировании в принципе, вспоминаем high cohesion и low coupling) между компонентами.
Ну и плюс в вашем подходе нужно разбираться, у него могут быть свои подводные камни, а с redux уже всё всем понятно и всё известно. Так что как по мне, не стоит изобретать велосипед в очередной раз.
Если так не хочется брать "такой большой" mobx, то лучше уж взять подход отсюда — https://habr.com/ru/post/491684/ — чем делать жёсткую циклическую связь сразу между всеми компонентами.
Чистый redux смысла нет использовать, можно подключить redux-toolkit (или reduxsauce) чтобы не писать много лишнего кода.
Для модификаций состояний можно использовать seamless-immutable. Это если не нравиться много писать.
В приведенном примере плохо — слишком большая связность между компонентами, и state все равно в компонентах. Я не представляю как бы я поддерживал и рефакторил такую архитектуру где были бы сотни компонентов.
Для модификаций состояний можно использовать seamless-immutable
Кстати, redux-toolkit идет в комплекте с immer.js от авторов mobx (совпадение? не думаю)
Это позволяет писать простой и понятный код в редьюсерах:
const reducers = {
resetStatusImmer = state => {
state.meta.status = 'initial'
},
resetStatusCommon = state => {
return {
...state,
meta: {
...state.meta,
status: 'initial'
}
}
},
}
Redux позволяет писать действительно большие проекты, MobX — быстрее стартануть.
Мда, куда катится мир. Они оба позволяют писать гигантские проекты. Что за «логика» такая?
Если ты не понимаешь что происходит в твоем коде, не умеешь пользоваться console.log, не умеешь пользоваться IDE (find references / find usages и т.п.), то у меня для тебя плохие новости и никакой тут mobx или redux не поможет.
Во-первых, Ваш подход вынуждает использовать компоненты-классы вместо компонентов-функций. Это может замедлить приложение, когда оно разрастётся, не говоря о том, что их сложнее дебажить.
Во-вторых, асинхронность. Если TicketDetails получает данные через HTTP, и если таких компонентов много (а их будет много), каждому придется добавлять какой-нибудь EventEmitter или Promise для синхронизации — а это дополнительный boilerplate-код и нарушение принципа single responsibility.
Ну и в-третьих, добавление жёстких зависимостей усложняет написание юнит-тестов, добавляя больше boilerplate-кода ещё и в них.
Во-первых, Ваш подход вынуждает использовать компоненты-классы вместо компонентов-функций. Это может замедлить приложение, когда оно разрастётся, не говоря о том, что их сложнее дебажить.
О, вы хотите об этом поговорить? Вот есть классы, которые, как мы все понимаем, после транспиляции превратятся в лежащую где-то коллекцию функций (статика) и коллекцию функций в «особой» группе prototype (не статика). А теперь, давайте с этого момента поподробнее — как именно это может замедлить приложение, когда оно разрастётся?
Чем одна функция, у которой внутри — та же самая коллекция, только в виде анонимных замыканий? Ну-ну.
Вы никогда не увидите разницу глазами в производительности между классовыми компонентами реакта и функциональными в реальных приложения.
Выигрыш одной из сторон на пару процентов вообще никакой погоды никому не делает.
А вот удобство разработки делает огромную погоду.
Я не к тому, что реактом пользоваться не надо, нет, прекрасная штука — нежно люблю и сам использую каждый день. Нет, я к тому, что вы разрабатываете на реакте, завязываясь на сам реакт. Ну и порождаете жесткую связанность(coupling), когда она вовсе не нужна.
Почему-то DDD пока мало проникает во фронтэнд, и мало кто отделяет доменные вещи от средства отображения. Все данные, всю логику, вообще почти все можно безболезненно отделить от реакта и это будет настоящим работающим ядром приложения без всяких отображений. Можно даже наверно сказать — это будет «реализацией стейт-менеджера», который по сути «переменная + сеттер + observable». Каждый может это сделать, а если проникнуться DDD — можно сделать это очень хорошо.
Так что не все находятся в ловушке, но многие, к великому сожалению.
Почему-то DDD пока мало проникает во фронтэнд, и мало кто отделяет доменные вещи от средства отображения.
Это не «почему-то», а как раз потому, что модные-молодежные фреймворки прошлого десятилетия (текущие топ-3 как раз оттуда) неявно способствовали отказу от MVC (и отказу от моделирования данных по DDD, соответственно) и пропагандировали идеи в духе «ты просто фигачь свой фронт целиком на нашем инструменте, а мы как-нибудь подумаем, чтоб тебе было норм». Разумеется, вторая часть в реальности для реакта никогда не воплощалась: реакт так и остался в первую очередь инструментом контроля за доступом к DOM (для чего он собственно и разрабатывался — чтоб парням в фейсбуке стало понятно, какой код и когда у них меняет DOM). Да и у других тоже не всё шоколадно — тот же ангуляр тащит свой корявенький DI (корявенький — потому что делался очень давно и не меняется из-за соображений обратной совместимости, хотя и сейчас можно сделать значительно более цивилизованно) ради архитектуры высокого уровня, но в вопросах моделирования данных просто говорит «вот у нас тут RxJS есть, уже впиленный в ангуляр — так что вы просто пользуйтесь им».
Текущее поколение «топовых» фронтовых фреймворков пытается подменять собой все части MVC, несмотря на то, что по сей день они в основном лишь отрабатывают букву V, а в части поддержки M и C у них у всех всё крайне небезоблачно.
НЛО прилетело и удалило эту запись, опс..
Что если components.ContentArea случайно изменится каким-нибудь другим компонентом?
Что если компонент более верхнего уровня будет размонтирован, а компоненты более низкого уровня — нет?
Кроме того, сложнее становится тестировать компоненты независимо, ведь кроме передачи пропсов нужно сформировать подходящий объект components с необходимыми заглушками, ценность этих тестов также падает.
Наконец, хранение ссылок на компоненты в глобальном объекте components, особенно массива ссылок (хоть автор и не рекомендует так делать), приведет к утечкам памяти, поэтому если уж и пользоваться таким подходом, то очень аккуратно, не забываю про очистку ссылок при размонтировании компонентов.
А вообще, в документации к react описано несколько подходов, позволяющих избегать сквозного проброса на несколько уровней вниз, включая использование контекста, рендер-пропсы и прочее, с описанием подводных камней и примерами.
Я думаю, что многие разработчики «морщатся» потому, что на интуитивном уровне понимают, что ваш подход противоречит принципам функционального программирования, в духе которого реакт и написан. И тут уже дискуссия перетекает на высокие материи =))
Почему это антипаттерн?