Pull to refresh

Comments 39

отвечая на вопрос in subject — возможно. надо просто смотреть шире
Можете написать свое хранилище на стейте и конекстах)
Вот сейчас хапну пачку минусов, но реакт со своими игрищами находится примерно там же, где и php году так в 2005. Flux/redux store — это прямой аналог $_GLOBALS, т.е. с точки зрения ООП — самый что ни на есть God object в наихудшем своём виде, когда он by design отвечает вообще за всё приложение. Модульность? Не, не слышал.
Потом, когда оказалось, что под капотом специально для redux в самом react есть контексты, даже если вы не используете redux, и скрывать этот факт стало совсем уж неприлично — контексты документировали. Т.е. открыли для всех аналог global-объявлений.
При этом родной для javascript способ хранения состояния объектов — ну, в самих объектах, считается фу-фу-фу и грязь.
Зато завезли хуки — сегодняшняя их версия выглядит нестрашно, но их нужно давить пока маленькие. Хуки приводят к самому дикому лапшекоду, который только можно представить, т.к. по сути представляют собой хорошо замаскированный goto. Посмотрите на сегодняшний вордпресс и особенно его интернет-магазин woocommerce.
Вместо того, чтобы выносить разработчикам мозг и заставлять их использовать одну конкретную архитектуру приложения, Абрамову и компании не помешало бы оглянуться вокруг и привести своё детище к современным стандартам. Баг про несовместимость реакта со стандартными модулями висит третий год — т.е. вы можете загрузить в браузер что угодно через script type=«module», включая подавляющее большинство компонентов для реакта, но не сам реакт. Уродский и сломанный во всех местах JSX вместе с jsx-компилятором давно пора закопать в пользу нативных js-шаблонов и template — как минимум, браузеры обрабатывают это быстрее. React-dom нужно уравнять по функционалу хотя бы с пятилетней свежести jquery, чтобы последний не тащили зависимостью в каждом первом проекте. Мечты, мечты…

Раз уж такая пьянка, то тоже хапну пару минусов.


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

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

При этом родной для javascript способ хранения состояния объектов — ну, в самих объектах, считается фу-фу-фу и грязь.

Хранить-то их там можно без проблем, вопрос лишь в том как вовремя замечать в них изменения? Тут нужно либо использовать протокол "сквозная иммутабельность + не создаем лишний раз новых объектов" (и тогда в пределе получится redux), либо использовать явные подписки на изменения (и тогда в пределе получится rx.js), либо использовать автоматические подписки (и тогда в пределе получится mobx). Все три способа возможны и реактом не запрещаются, а других способов нет.


Уродский и сломанный во всех местах JSX вместе с jsx-компилятором давно пора закопать в пользу нативных js-шаблонов и template — как минимум, браузеры обрабатывают это быстрее.

Это невозможно сделать сохраняя основную идею реакта (метод render). Точнее, возможно — но будет медленнее, а не быстрее.


React-dom нужно уравнять по функционалу хотя бы с пятилетней свежести jquery

Например?..

а других способов нет

Computable и Observable поля были ещё в knockout.js и самом-самом первом angular.js, существовавших до реакта.
Особенно смешно про иммутабельность. Примерно все умеют в Object.freeze, который как раз про иммутабельность, кроме react.js.
Это невозможно сделать сохраняя основную идею реакта (метод render). Точнее, возможно — но будет медленнее, а не быстрее.

HTML тэг template парсится один раз при загрузке браузером. То же самое, гипотетически, возможно с `литералами`. Дальше они клонируются, а не создаются. Это тот самый виртуальный дом, только реализованный движком браузера и быстро, а не через вызовы CreateElement.
Например?..

Ну например один раз при старте приложения повесить один обработчик onChange на все поля ввода определённого класса, а не вешать свой handleChange на каждое поле при создании, а потом уничтожать при удалении из DOM.
Ну например один раз при старте приложения повесить один обработчик onChange на все поля ввода определённого класса
Зачем пихать в react то, что решается одной строчкой кода?
if (!e.target.classList.contains(cls)) return;
Computable и Observable поля были ещё в knockout.js и самом-самом первом angular.js, существовавших до реакта.

Ну так идейным наследником knockout как раз mobx и является.


Особенно смешно про иммутабельность. Примерно все умеют в Object.freeze, который как раз про иммутабельность, кроме react.js.

Иммутабельный объект — это объект, свойства которого никогда не меняются. Причина, по которой они не меняются (к нему применили freeze, или их просто не меняют) — не важна.


HTML тэг template парсится один раз при загрузке браузером. То же самое, гипотетически, возможно с литералами. Дальше они клонируются, а не создаются. Это тот самый виртуальный дом, только реализованный движком браузера и быстро, а не через вызовы CreateElement.

А дальше что? Как обновлять результат такого "рендера"?

Со всем не согласен =)


Хуки в React удобны (раза в два короче классов, лучше читать лапшекод с хуками, чем лапшекод с классами), только построены на той самой $_GLOBALS к сожалению для того, чтобы не ломать код. Если бы поменяли api на что-то вроде


const MyComponent = (props, io) => {
  const [value, setValue] = useState(io, 'default value');
};

стало бы лучше, это ЯВНЫЙ способ закрепить что-то на узле virtual dom. Сейчас этот io — объект в react модуле (сразу лазил смотреть, как сделали). Тестировать не удобно и тп.


Контекст нормальный и это не GLOBALS, потому что контекст доступен только в поддереве. Удобный механизм прокидываний props глубоко внутрь (не нравится — кидайте руками и получите по 100500 props на верхнем уровне).


JSX тоже нормальный, если целиком понимать, что внутри это React.createElement(MyComponent, myProps), просто сахар. И я хотел бы такой сахар например в c++ когда нужно делать подобные деревья (scenegraph в играх). К сожалению большая часть народа, которая хочет зп до 150к не понимает этого вообще (мой опыт).

Flux/redux store — это прямой аналог $_GLOBALS, т.е. с точки зрения ООП — самый что ни на есть God object в наихудшем своём виде, когда он by design отвечает вообще за всё приложение.

А вы не могли бы предложить альтернативный способ построения приложений, чтобы можно было более предментно обсуждать?
MVVM как в конкурирующих фреймворках, например.
А как MVVM регулирует взаимодействие разделяемого несколькими компонентами состояние модели? Потому что без него и в React отдельный менеджер состояний не нужен. Хватит аккуратного разделения на умные компоненты со стейтом и глупые с JSX, это даже линтером наверное можно настроить.
Прелесть MVVM в том, что у вас «из коробки» отношение многие-ко-многим между моделями и вьюхами. Надо вам несколько вьюх к одной модели — да пожалуйста, как и наоборот. Всю «магию» по разруливанию связей берёт на себя слой ViewModel, и да, да, двустороннее связывание.
С JSX, точнее redux, у вас дерево и одностороннее связывание. И довольно часто ближайшим общим предком компонентов, которым нужно общее состояние, является корневой объект. Для компонентов с собственным состоянием начинаются пляски с бубном, все эти UNSAFE_componentWillReceiveProps и попытки определить, кто именно вызвал изменения пропсов — сервер, сам компонент или что-то другое, чтобы не уйти в бесконечный цикл. А ещё такая схема часто вызывает перерендеринг всего, т.к. в реакте мелкое сравнение на изменение пропсов. И не надо рассказывать, что это быстро.
Я не уверен, что правильно понял, но чем это отличается от Redux, где модели — это ветки стора с редьюсерами, контейнеры вместе с connect, mapStateToProps и mapDispatchToProps — это ViewModel, а глупые компоненты — View?

определить, кто именно вызвал изменения пропсов — сервер, сам компонент или что-то другое

С точки зрения менеджера состояний — должно быть ясно из названия вызванного экшена или просто из того в какой функции модели вы находитесь. С точки зрения стандартных глупых компонентов — должно быть пофигу. Специально вводили виртуальный DOM чтобы не заниматься обходом всех пропсов и смотреть какой кусок DOM менять, как бывало в первом AngularJS. Либо ссылки на пропсы поменялись и обновляем всё, либо — нет, и мы ничего не трогаем.

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

Jsx — это всего лишь сахар. Лично люблю его изнасиловать и подсунуть ему свою h функцию, после чего получить <span/> = HTMLElement с детями и без необходимости руками проделывать рутинные операции по сборке дерева.
Прикол в том, что кроме JSX в голом реакте есть ещё ровно два класса — react и reactComponent :) Т.е. сам по себе react.js пустой до неприличия

Можно ли жить без ног? Можно, но с ногами удобней. Если они есть, то отказываться от них глупо. Тогда уж отказывайся и от рук, чтобы "свобода" была полной. Имхо.

Так то MobX есть, как альтернатива ридаксу. Он то как раз про observable и observers и не принуждает к конкретной архитектуре. Просто удобный способ, связать данные в стейте и компоненты
Mobx хорош. Очень небольшой его минус: 1) изменяет view, добавляя декоратор и 2) если обновляются k зависимых полей, метод render вызывается k раз (что не проблема для react)
изменяет view, добавляя декоратор

Но ведь вам в любом случае нужно что-то менять во view, когда вы меняете интерфейс модели...


если обновляются k зависимых полей, метод render вызывается k раз (что не проблема для react)

Давно уже придуман action, который решает именно эту проблему

Component {
  props.model
  props.onModelChange(fieldName, value)
}


@observer
Component {
  props.model
}


Дело вкуса.
Не нравится, что во втором варианте любой (не имеющий отношения к модели) observable props будет форсить компонент к перерисовке.

А откуда у вас в нормальной программе возьмется "не имеющий отношения к модели observable props"? И что, по вашему, должен сделать правильный компонент, когда используемая им при рендере информация изменилась? Неужели проигнорировать это изменение?


Кстати, информация о fieldName — бесполезная. Реакт просто не умеет обновлять компонент по частям, метод render() можно вызвать лишь целиком.

Хмм… MobX отличает observable поля state и props от других observable полей объекта (e.g. this._counter)

информация о fieldName — полезная. onModelChange обычный callback в child-parent communication

Ну да, отличает. Но ведь при любом изменении props Реакт и без mobx перерисует компонент (если это PureComponent или Component без shouldComponentUpdate), так в чем отличие именно mobx?

Все верно, вы правы.

У меня была ошибочная гипотеза:
@observer
class Component {
  @observable _counter = 0;
  
  someFunction() {
   if (nothingChange) {
      this._counter++; //  I thought that it should force rerender 
      return;
   }
   ...
  }
}
Я сам всегда примерно redux и недавно попался чужой проект на monx после чего скорее всего буду с ним работать. Но redux ведь тоже как минимум оборачивает в декоратор connect компонент react

Это теперь называется "не меняет"? :-)

Разумеется, сам рендер меняется. Так же как и при использовании нативных контестов, например. Я имел ввиду, что он не добавляет никаких магических декораторов. Это более прозрачный подход и позволяет следить за локальными изменениями в дереве, а не всем компонентам.

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


Observer component, к слову, в mobx-react был примерно всегда.

А при чем здесь хук? Про него речи вообще не было. Никто им не заставляет пользоваться.

При том, что поддержка хуков — единственное что появилось в mobx-react-lite по сравнению со старым mobx-react.

Не только возможно, а гораздо лучше без Redux. Я переводил с Redux на MobX два приложения в Яндекс и UBS — субъективно скорость разработки выросла раза в три за счёт избавления от бойлерплейта. И самое главное из за чего мы инвестировали уйму времени на миграцию на втором проекте — производительность UI значительно выросла и вернулась к O(n), где n — число компонентов для рендеринга. В Redux каждый reducer обрабатывает каждый action, что даёт квадратичный рост времени вычислений. То что Redux подходит для больших приложений это не правда, так как в действительно больших приложениях у вас вырастают требования к быстродействию каждого компонента. Можно перепесать механизм композиции редюсеров и избавиться от сложности O(n ^ 2), но производительность продолжить есть копиривание объектов для достижения иммутабельности. Кроме того, Redux нарушает фрактальную природу программ, когда части имеют ту же структуру, что и целое, предлагая глобальный стэйт без возможности инкапсуляции. Redux хорошо распиарен, но я не рекомендую исспользовать его в серьезных проектах, так как количество архитектурных багов там слишком велико. Вы их победите конечно, но потеряете время.

Полностью согласен с


не рекомендую использовать его в серьезных проектах

Все дело в инкапсуляции. На мой взгляд это самый серьезный недостаток Redux для крупных проектов. Store один, поэтому


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

Я тоже переводил проект с Redux на свою архитектуру (очень похожа на MobX, только тогда его еще не было, поэтому пришлось писать самим). Тоже заметил, что


скорость разработки выросла раза в три за счёт избавления от бойлерплейта
В Redux каждый reducer обрабатывает каждый action

Это зависит от того как вы ваш редьюсер приготовите. Совершенно не обязательно писать его "классически". Redux не навязывает вам того как редьюсер должен быть устроен. Лично я предпочитаю 1 action = 1 handler, а сам reducer как древо hash-map вида [action.type piece]: handler. В таком случае никаких O(n), никаких switch-case.


есть копиривание объектов для достижения иммутабельности

А тут асимптотика равна глубине древа. Т.е. совсем чуть-чуть.


Проблема с производительностью в redux кроется скорее в том, что react-redux-ий connect написан так, что все его instance-ы вызываются на любое изменение store. Это ограничение преодолимо, за счёт написания своего иерархического HoC, вместо react-redux.


Ну и в целом сделать большое приложение с иммутабельным древом производительным стоит кучи геморроя в поддержке кеширования промежуточных вычислений, обеспечения бесперебойной работы React.memo | PureComponent. Это мягко говоря не тривиально и пестрит кучей нюансов. Мало кто умеет это делать правильно. Хуже того — мало кто пытался пробовать.

Sign up to leave a comment.

Articles

Change theme settings