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

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

Когда следует использовать Redux?

Никогда. React нужно использовать с MobX.

А я вот считаю с точностью до наоборот :)


  • Во-первых, MobX в 7 раз толще Redux (15kb vs 2.5kb по версии Bundlephobia).
  • Во-вторых, при попытке сделать с ним что-то нестандартное он просто взрывается (ну, по крайней мере так было года три назад, когда я последний раз его трогал).
  • Ну и вообще, чем меньше магии, тем проще работать с инструментом. MobX в этом смысле магией забит под завязку. А где магия — там и костыли (Observable Arrays всё ещё там используются?)

Хотя, конечно, среди всех менеджеров состояния мой личный выбор — это вообще storeon. Маленький, но при этом мощный и ничем не уступает Redux, а где-то даже превосходит.

Во-вторых, при попытке сделать с ним что-то нестандартное он просто взрывается (ну, по крайней мере так было года три назад, когда я последний раз его трогал).

Лол, шта?? Как обычно пустые слова без конкретики. И разумеется стандартная отмазка аля «ой, да я не помню, это было много лет назад, но я точно не вру, просто поверьте мне на слово»

Во-первых, MobX в 7 раз толще Redux (15kb vs 2.5kb по версии Bundlephobia).

+15kb в обмен на невероятное удобство — смешно. Тем более вы сэкономите их т.к. вы как минимум тупо по кол-ву кода сильно меньше будете писать используя MobX.
Ну и вообще, чем меньше магии, тем проще работать с инструментом. MobX в этом смысле магией забит под завязку. А где магия — там и костыли (Observable Arrays всё ещё там используются?)

Вообще никакой магнии. Всё элементарно и просто — getters/setters.
Вот ссылочка, если вы не в курсе что это такое. В JS существует очень много лет.

Хотя, конечно, среди всех менеджеров состояния мой личный выбор — это вообще storeon. Маленький, но при этом мощный и ничем не уступает Redux, а где-то даже превосходит.

Сочувствую.

Да чего вы так возбудились-то, будто я у вас священную корову только что отобрал :D


Лол, шта?? Как обычно пустые слова без конкретики.

Предлагаете пример с вас брать? Вы-то тут так много пруфов привели, что я снимаю шляпу :D


+15kb в обмен на невероятное удобство — смешно.

Мне становится жаль пользователей вашего продукта.


Тем более вы сэкономите их т.к. вы как минимум тупо по кол-ву кода сильно меньше будете писать используя MobX.

"Лол, шта?? Как обычно — пустые слова без конкретики"


Вообще никакой магнии.

Слова про Observable Arrays вы, видимо, специально игнорируете ))


Сочувствую

Спасибо, заберите обратно :D

Во-первых, MobX в 7 раз толще Redux (15kb vs 2.5kb по версии Bundlephobia).
Не дальновидный аргумент. А вместе со всякими сагами, ресектами и прочими библиотеками, которые необходимо использовать с Redux, те же 2.5kb останутся?
Ну и напишите, например, кода на Mobx на 100kb, а на Redux то же самое будет занимать 300kb.
Итого, проект займет 115kb на Mobx против 300kb+ с Redux. Ну 150kb+ с Redux-toolkit.

Ну и вообще, чем меньше магии, тем проще работать с инструментом. MobX в этом смысле магией забит под завязку. А где магия — там и костыли (Observable Arrays всё ещё там используются?)
С появлением react хуков, это странно читать. Большинство используют хуки, при этом не понимая, как они внутри устроены. То есть react hooks для большинства не меньшая магия, но что-то это особо не подается как недостаток.
Ну и мне, честно говоря, не понятно, что именно многие в MobX называют магией? Использовать обертку для наблюдения за изменением данных или что-то другое? Ну, думаю автору Mobx не помешало бы хорошее описание внутреннего устройства Mobx, чтобы меньше говорили про магию, но для react хуков подобного описания тоже нет.
Не дальновидный аргумент. А вместе со всякими сагами, ресектами и прочими библиотеками, которые необходимо использовать с Redux, те же 2.5kb останутся?

Честно скажу, что саги, реселекты etc точно так же не особо приветствую. По-моему, самое лучшее — это thunk и голый редакс. И нет, не могу сказать, что там сильно много бойлерплейта получается. Всё, как всегда, зависит от правильной структуры.


С появлением react хуков, это странно читать.

Ну, здесь речь изначально шла о Redux vs MobX. Но моё мнение, что да, с хуками всё та же проблема. У реакта и реакт-комьюнити почему-то большая склонность к магии в целом.


Те же саги — так и не понял их смысл. Санки и работают, и тестируются не хуже (а то и лучше). А весит эта библиотека что твой самолёт.


В общем-то, поэтому-то мне и нравится storeon — мало того, что он весит 180 байт, так ещё и простой как табуретка, и никаких дополнительных приблуд ему не нужно. Всё, что нужно, сразу есть.


Ну и мне, честно говоря, не понятно, что именно многие в MobX называют магией? Использовать обертку для наблюдения за изменением данных или что-то другое?

Ну, в целом, да, у меня первой вспоминается именно Observable Arrays. Помню, у меня довольно сильно подгорело, когда пришлось каждый раз этот дурацкий массив туда-сюда преобразовывать. При том, что Array.from тогда не сильно ещё использовался.


Вторым, кстати, был autorun. Именно с ним было связано моё впечатление, что с MobX тяжелее сделать что-то нестандартное.

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

А зачем тут Array.from?


Вторым, кстати, был autorun. Именно с ним было связано моё впечатление, что с MobX тяжелее сделать что-то нестандартное.

А с ним-то что не так?

По-моему, самое лучшее — это thunk и голый редакс.

Просто идеальный комплект для быдлокодерства.


И нет, не могу сказать, что там сильно много бойлерплейта получается. Всё, как всегда, зависит от правильной структуры.

Там не "сильно много". Там "тьма" бойлерплейта. Там бойлерплейта это 90% кода. Я работал с кодом на танках. За полгода разработки 500 000 строк кода. Если написать этот же код на чем-то адекватном, то там будет кода ровно в 10 раз меньше. А всё потому что код писался ровно по заветам Дана Абрамова.

А всё потому что код писался ровно по заветам Дана Абрамова.

Да уж, он тот ещё говнокодер. И увы почему-то люди думают что он пишет хорошо и правильно и пытаются ему подрожать.

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

Ну странно пользоваться библиотекой и при этом не пользоваться советами создателя этой библиотеки по поводу её использования. Вы когда технику покупаете — тоже принципиально игнорируете то, что написано в инструкции?

Одно дело официальная инструкция и совсем другое чьи-то личные заметки в твиттере "смотрите какой прикольный паттерн получился".


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

Вы лучше своё покажите. Какой там классный, божественный код на редаксе получается. Короткий, совсем как на МобИкс. Я вон ниже пример написал — всего 7 строк кода. Посмотрим на творение гениальных редакс-инженеров.

Не могу показать вам код на редаксе, потому что я им тоже не пользуюсь
¯\_(ツ)_/¯


Я стригерился на эту цитату:


А всё потому что код писался ровно по заветам Дана Абрамова.

Всё-таки нельзя все списывать на кого-то там, нужно и самому думать

Весь редакс написан по заветам Абрамова, что тут списывать? Я и тех документации ничего хорошего не вижую

Просто идеальный комплект для быдлокодерства.

Набыдлокодить можно с чем угодно. И MobX/Redux Saga etc тут не спасут никак.


Там не "сильно много". Там "тьма" бойлерплейта.

Если по примеру рекомендаций в документации класть константы в один файл, редьюсеры в другой, а action creator-ы в третий — тогда да, чёрт ногу сломит, и довольно быстро. Не понимаю этого функционального деления. А если делить логически, то получаются небольшие аккуратные файлы, которые вряд ли будут больше классов MobX. Я ж говорю, всё зависит от прямоты рук.

А если делить логически

То количество кода — то же, просто в других файлах.


которые вряд ли будут больше классов MobX

Они 100% будут больше классов MobX. Ну вот, к примеру


class Article {
  @observable text: string;
  @observable mark: number;
  @observable comments: Comment[] = []
}

class Comment {
  @observable text: string;
  @observable mark: number;
}

Всё, у вас рабочий код на МобИкс. Добавьте сюда связь с сервером и прикрутите вьюшку на реакте — всё будет работать. А теперь попробуйте переписать этот код со всем бойлерплейтом на Редаксе. Вот без сервера, чисто API на модель, которая позволяет менять стейт.


А потом учтите, что в МобИкс есть кучу классических приёмов для реюза кода, которые в редаксе просто не работают. Как итог, редакс — это бойлерплейт + копипаст, а МобХ — отличный читабельный код.

Честно скажу, что саги, реселекты etc точно так же не особо приветствую. По-моему, самое лучшее — это thunk и голый редакс.

Вы в курсе, для чего нужен реселект? К голому редаксу для оных целей понадобится прикрутить всякие memoize-one и т.д., либо писать это всё совсем вручную (самый дурацкий вариант).

Вы так говорите, как будто я его никогда не использовал ) Конечно, в курсе. Вот только нужен ли он? Зависит от задачи. Иногда проще считать данные прямо на месте, через useMemo.


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

Честно скажу, что саги, реселекты etc точно так же не особо приветствую. По-моему, самое лучшее — это thunk и голый редакс.

Есть подозрение что вы не понимаете концептов redux. Ибо использовать его в хоть сколько бы то ни было сложном проекте вообще без мемоизации просто нельзя. Ну если вам не плевать на проект.


Буду рад, если я неправ.

Observable Arrays всё ещё там используются?

А что с ними не так? Особенно в новой версии, которая использует прокси?

С прокси, наверное, вполне нормально, но когда там был объект, изображающий из себя массив, было больно )

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

Ни разу не видел. Хорошо бы пример. Навскидку могу назвать только такой антипаттерн: сделать поле @observable (который по факту — deep), хранить там громадную развесистую структуру и обновлять её в иммутабельном редуксовом стиле. Вроде бы тогда должно дохрена пересчитываться.

А как это правильно сделать, при условии, что в @observable поле могут лежать совершенно разные структуры данных?

У меня именно такая задача. Надо реагировать на изменения внутри этих структур данных. И, да, обновляются эти структуры в иммутабельном стиле.

Ну и что совершенно разные структуры данных делают в одном поле?


Что же до обновлений — тут всё просто. Или всё-таки переписываем иммутабельный стиль на мутабельный (как правило предпочтительнее, но иногда дольше) — или используем @observable.ref

Что делают? Лежат.

Есть виджеты с разными типизированными полями, логикой и т.п. Для работы с этими виджетами есть класс обёрнутый в mobx. У каждого виджета есть поле data в котором хранится некая структура данных, специфичных для этого виджета. С точки зрения класса без разницы, что именно там лежит, он должен уметь эти данные отдавать, получать, и уметь реагировать на обновления.

про @observable.ref почитаю, спасибо

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

По сути, в нашей реализации виджеты uncontrolled. У них есть своё состояние, которое они могут «наверх» в mobx класс сохранить. А вот дальше уже класс может запустить некоторую логику, по результату работы которой стригерить, например, сохранение данных этого виджета на сервер.

Представьте большой конструктор форм, в котором самые разные виджеты. Календари, гриды, инпуты всех видов, upload файлов, всё это с какой-то логикой и связями между полями. У каждого виджета есть своя json схема валидации.

mobx класс всем этим дирижирует: сохраняет на сервер данные виджетов в фоне, сообщает внешним слушателям о валидности формы, управляет подсветкой ошибок, и т.п.

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

А виджеты внутренние или внешние? Если внутренние — наверняка можно сделать проще. Если же внешние — то да, тут observable.ref для данных нужен.

Только вот с оптимизацией у Redux я бы сказал абсолютно никак, со структурированием данных тоже не очень. Помимо голого Redux надо тащить за собой ещё пачку библиотек, ибо отдельно юзабелен только для очень мелких проектов.

RxJS по вашему — это магия богов? И простому смертному туда лезть не стоит? Это я так, к примеру.
RxJS очень плохой пример) Тем более в мире React'a. Категорически нельзя тащить RxJS в React приложение, но если разработчик мазохист, то почему бы и нет)

mobx лучше redux тем, что он позволяет писать меньше кода, и этот код будет работать сразу оптимальней чем с redux. Чтобы добиться сопоставимой с mobx производительности в приложениях (больше чем hello-world), нужно написать много нетривиального кода внутри reselect/memoize/shouldComponentUpdate. И даже с этими оптимизациями redux на каждое обновление стейта будет обходить все connected компоненты (сложность O(n)), когда внутри mobx будет чёткий список зависимостей и будут обновлены только компоненты зависящие от изменённой части стейта.


Вот можно сравнить два проекта и количество и сложность кода:
https://github.com/mweststrate/redux-todomvc
https://github.com/mweststrate/mobx-todomvc
image

НЛО прилетело и опубликовало эту надпись здесь
у обоих источники активные — можно нагородить иерархические связи с источниками и получить не отлаживаемое решение когда вообще не понятна структура и иерархия связей + можно получить бесконечный цикл

А вы не делайте так, и всё в порядке будет.


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

Ну так пишите используя хуки, в чём проблема-то?

Спасибо за подробное объяснение!

Вот прям как раз недавно была статья про то, что context + useReducer не лучшая идея для управления состоянием.

Очень правильная, важная и отрезвляющая статья.
А у вас нет перевода на английский?

Оказалось, что сверху указано, что это — перевод с английского.

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

Вы меня прямо заинтриговали. Полез в код. Но нет. Никакой магии. У всех одно и тоже. Есть глобальная переменная с map от context to listeners. Есть обычный React.createContext со 2-ым аргументом. И там просто цикл по listeners с ручным вызовом forceUpdate. В чём принципиальная разница с Redux и прочими решениями?


Тут главная проблема в том, что реакция на смену состояния происходит путём ручного вызова forceRender-ов компонентов с подписками. Причём просто тупо в цикле. Если у вас есть другие context провайдеры, которые просто не успели отреагировать (какой-нибудь react-router) вы получите всё те же race-condition-ы.


В общем я не соглашусь, что "для контекста давно есть недокументированные возможности для вызова изменения лишь того компонента, который использует контекст чей селектор подтвердил что данные в нем изменились". Ибо то, что есть — это грубый костыль который лишь отчасти решает проблему. И то путём реализации собственных глобальных Observable. По сути context в таком решении играет опосредованную роль.


А нормальный контекст с частичной подпиской, имхо, может сделать только команда React. Остальным не хватает доступа.

Уточню, что я имею ввиду. Должно быть примерно так:


const context = createContext(initValue, (oldValue, newValue, storage) => {
  const { selector } = storage;
  return isShallowEq(selector(newValue), selector(prevValue)) !== true;
}

Т.е. чтобы этот недокументированный callback вызывался для каждого отдельно взятого компонента самим React-ом. И только тогда когда вышестоящие звенья DOM-древа уже обработаны. Т.е. параллельные контексты тоже могли успеть обновиться и обновить свои подзвенья до текущей глубины. Ну и от callback-а достаточно просто вернуть true или false, в как shouldComponentUpdate.


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

НЛО прилетело и опубликовало эту надпись здесь
опыт промышленного использования данного контекста в проекте не выявил ни одного минуса — только плюсы — малый размер кода и простота использования

Это вы ещё MobX не пробовали, попробуйте и только тогда узнаете, что такое малый размер кода и простота.
НЛО прилетело и опубликовало эту надпись здесь
данный пример не является лучшим — меньшее количество кода при потенциально больших проблемах при взаимодействии большого количества observables — которы кроме как чтения всего кода нигде не декларируется

Какие ещё проблемы вы о чем? Тем более вы говорите большие проблемы, интересно. На практике хоть раз в жизни реализуйте проект с MobX'ом и увидите что проблем то и нет никаких вовсе, одно лишь удовольствие.

А то прочитали где-то про какие-то там потенциальные проблемы, которых нет. Все проблемы — кривые ручки которые не умеет строить архитектуру приложения.
Если бы были проблемы, то никто бы и никогда не использовал MobX, я в том числе. Но вот незадача, проблем то нет, от слова совсем.

которы кроме как чтения всего кода нигде не декларируется

Что простите??? Что значит нигде не декларируется?? Вы вообще о чем??

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

Во первых использование классов и декораторов в MobX'e опционально, всё тоже самое там есть с обычными функциями.

Никто не мешает совмещать hooks с MobX'ом. Ну сразу же видно что вы даже ни присосались к MobX'у, вы считаете что эти вещи несовместимы.

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

Чуть чуть более многословен?? Жесть.

Это однонаправленный поток данных?
function handleChange(e) {
    store.dispatch({
        type: 'SET_INPUT_VALUE',
        payload: e.target.value,
    });
}
<input onChange={handleChange} />

setInterval(() => {
    store.dispatch({
        type: 'SET_INPUT_VALUE',
        payload: 'value from interval',
    });
}, 1000);

А это однонаправленный поток данных?
class Store {
    @observable text = '';
    handleInputChange = (e) => {
        this.text = e.target.value;
    }
}
const store = new Store();

<input onChange={store.handleInputChange} />

setInterval(() => {
    store.text = 'value from interval';
}, 1000);


И ещё на всякий случай, вдруг не знали:
Подсказка, WebStorm и VS Code умеют сто лет в обед — «Find References / Find Usages» где сразу вино в каких местах переменная читается, а в каких местах переменная изменяется. Теперь когда вы узнали эту древнюю фичу, ещё раз подумайте какие там у вас «потенциальные» проблемы с observables есть? Вот просто интересно, какие же??
AZaz1 ответов на вопросы я так понимаю не будет да?
НЛО прилетело и опубликовало эту надпись здесь
по поводу кода что вы привели — он явно преувеличен — никто команды в виде store.dispatch({...}) не отправляет — для этого есть action creator

Да, это ещё одна примера усложнения кода на redux.


последний пример никак не относится к однонаправленному потоку данных в redux — это пример множества источников команд — поток при этом остается однонаправленным (пояндексите "redux однонаправленный поток данных")

Фокус в том, что и в mobx поток точно так же остаётся однонаправленным, если намеренно не создавать циклов:


action -> … -> action -> observable -> computed -> … -> computed -> reaction

НЛО прилетело и опубликовало эту надпись здесь
есть еще один поток: когда один observable может быть подписан на изменения другого, а тот в свою очередь еще на одного и т.д. — мы делаем изменения в одном месте и порождаем дерево потоков как в стор так и между observables — и это уже не однонаправленный поток данных и отследить его — задача для эквилибриста)

С редуксовыми миддлварями можно не менее забористого спагетти наварить.


bad practice — мутирование объектов.

Зависит от ситуации.

НЛО прилетело и опубликовало эту надпись здесь
observables — это основной паттерн работы с MobX

А изменение observables в авторанах прочих реакшенах (вы ведь об этом?) — один из основных антипаттернов, о чем написано в TFM. По возможности, все реакции — переход из мобикса в не-мобикс. Для остального есть мастеркард computed.

есть еще один поток: когда один observable может быть подписан на изменения другого

Просто не делайте так.


bad practice — мутирование объектов

С каких это пор?

когда один observable может быть подписан на изменения другого

А это как? Ну физически как? В MobX есть @observable — свойства на которые там где-то внутри происходит подписка и @computed — свойства, которые подписываются. Вы не подписываете никакие observable на другие observable.

В документации к use-context-selector указано:


Limitations. The stale props issue can't be solved in userland.

Т.е.


в отличие от redux все регистрации компонентов происходят в порядке их нахождения в дереве

Я, поначалу, хотел было сказать, что у них одинаково устроена регистрация компонентов, но вот теперь не уверен. У use-context-selector оно устроено просто как топор — Map<Context, Set<Listener>>. А у react-redux там какие-то деревья. Но регистрируются они в одном и том же месте — в useEffect-е.


Я полагаю что если React в принципе позволяет вызывать эффекты у детей раньше родителей, то они оба подвержены stale props. Обе библиотеки не могут отследить своё место в дереве. В обеих подписка устроена в useEffect.


тут не происходит обновлений компонентов путем ручного forceRender — тут после изменения стейта в контексте идет анализ изменений стейта и вызов setState лишь у тех компонент чьи селекторы вернули новое значение

А в чём разница? В redux.useSelector там useReducer. Суть точно такая же. Так обычно forceUpdate и делают в функциональных компонентах.


Про observedBits пока не смотрел, сегодня не успеваю. Завтра посмотрю. Точнее я уже вникал в каком-то прошлом споре про context… но уже всё забыл :)

НЛО прилетело и опубликовало эту надпись здесь

Я воспроизвёл зомби с use-context-selector-ом версии 1.4.2 (код прямо из репы). Всё работает именно так, как и я ожидал. Приложение просто падает:



Тестовый код. Код написан ногами, sorry. Просто сделайте git clone && npm ci && npx webpack-dev-server. Это чисто proof-of-concept. По сути я просто видоизменил код из статьи чтобы было удобнее разбор полётов проводить.


Собственно я ничего особенного и не сделал там. Обратите внимание на вывод в консоль на скриншоте выше.


  • По-умолчанию у нас в state лежит { a, b, c }.
  • По нажатию на кнопку deleteB мы убираем из {a,b,c} наш b и получаем {a,c}. Примерно как в той статье.
  • Это приводит к смене context.Provider value=%o значения.
  • Это в свою очередь запускает тот самый цикл с проверками на изменённость состояния.
  • А в них сравнивается новый результат селектора со старым. Если они равны — forceRender не вызывается.
  • В нашем случае для ItemB всё тупо падает т.к. <ItemB/> вообще должен быть к этому времени уже удалён. Но он не успевает удалиться. Ибо его селектор вызывается раньше. С уже новым state-ом при котором его (ItemB) в живых быть уже не должно.

В общем те самые stale props и zombee children. И ровно тоже самое что я вижу в ваших статьях. Можно легко видоизменить код под useSelector в react-redux и возможно он точно так же упадёт. А может те костыли с try-catch нас спасут :) Но на любую хитрую гайку найдётся болт с резьбой.


Было ещё интересно почему render ItemA вызывается повторно. Оказалось React ловит эту ошибку и считает — а попробую ка я ещё раз отрендерить <App/>, вдруг на этот раз прокатит. И вот когда он падает 2-й раз — он падает уже по-настоящему. Любопытно.

Ещё одна любопытная деталь про useContextSelection (версии 1.4.2). Оно пролюбливает любые обновления своего селектора. Там тупо он нигде не обновляется и никак не фигурирует в deps. В итоге мои тесты показывают что селектор для компонента задаётся ТОЛЬКО во время изначального рендера.


Это в какой-то степени решение проблемы stale-props. С таким подходом они stale по-умолчанию :-) Вы правда используете это в продакшне? Или это свежие версии либы такие?

НЛО прилетело и опубликовало эту надпись здесь
ничего не могу сказать по use-context-selectOR — мы используем use-context-selectION

Я его код и взял. Версия 1.4.2, как по ссылке. Просто склонировал репозиторий. Вы мою демку даже не открывали?


автор use-context-selection верно написал, что пакет не может исправить проблему со stale props, но опять же повторюсь, не он ее и порождает.

Не понимаю о чём вы. Кто что порождает? Как я вижу проблему этих zombie — обе библиотеки вызывают setState\dispatch в цикле, не тогда когда дело доходит до нужных компонент, а прямо сразу после после смены состояния. Отсюда и все недостатки. Это всё идёт в обход React, даже если это сделано в рамках callback-а к createContext.


Выше вы писали:


в отличие от redux все регистрации компонентов происходят в порядке их нахождения в дереве, поэтому вызов обновлений их стейтов никогда не приводит к race condition в отличие от redux, который порождает zombie children из-за обратного порядка подписок на изменения стора

Я посмотрел и увидел что регистрация устроена похожим образом — в useEffect. Поэтому усомнился в ваших словах. Склонировал проект и воспроизвёл zombie children. Самым простым образом. В общем я до сих пор не понимаю вашу мысль.

НЛО прилетело и опубликовало эту надпись здесь

Если я уберу эти библиотеки, то у меня не будет zombees. Будут лишние обновления тех компонент, которые не зависят от изменённых значений. Но зомбей не будет.


Context в рамках своих задач работает хорошо. Он не умеет в partial updates, но в full updates он умеет хорошо. Без stale props, без zombees.


Эти библиотеки делают все то что делает контекст за одним исключением — вызывает обновление не всех поголовно подписанных на него компонент а лишь тех у кого селектор получил новые значения

За двумя исключениями, не за одним. Они делают это "через задницу". Отсюда и zombee. Но не потому что авторы плохие, а потому что API для этого нет.

НЛО прилетело и опубликовало эту надпись здесь
каким же это образом им удается это создать если они делают все те же обновления

Да очень просто. Вызов callback-а в createContext осуществляется сразу после смены состояния во время render-а Provider-а. А убирание звеньев из DOM-Tree происходит уже в рамках React-render-а. Так как вызов selection-ов для всех listeners осуществляется до render-а subtree — оно падает ещё до того, как эти звенья из дерева удаляются.


Откройте демку наконец. Там кода кот наплакал.


И я не пишу сейчас ничего нового. Это же самое написано в тех статьях ссылки на которые вы сами и привели.

НЛО прилетело и опубликовало эту надпись здесь
они появятся там когда в callback будет вызван setState

Всё падает ещё на стадии вызова селектора внутри либы. Который проверяет нужно ли обновлять компонент. До setState не доходит.


разбирайтесь в своем коде

Я уже несколько раз пожалел что стал вам отвечать. У вас заслуженные -25. Вы мало того что не понимаете о чём говорите, вы даже других не пытаетесь слушать. Вы, блин, даже демку открыть не можете.


Жаль, здесь до сих пор нет игнора.
И да, пример делать не надо. Давайте закроем тему.

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

Это например касается MobX'a, что с ним якобы большие проблемы, пишете что мутирование объекта это bad practice(что вообще просто нелепо) и бла бла бла.

По поводу вашего любимого use-context-selection:
Вам же написали конкретику и скинули конкретный код, при чем маленький, а вы даже не удосужились его посмотреть, это бы отняло от силы 5 минут. Но продолжаете спорить.
Это например касается MobX'a, что с ним якобы большие проблемы, пишете что мутирование объекта это bad practice(что вообще просто нелепо) и бла бла бла.

И подписки обзервбла на обзервбл, на что ответ так и не был дан.

НЛО прилетело и опубликовало эту надпись здесь

Спасибо за пример. В нём есть Zombee. А не падает оно потому что ваш селектор в случае обращения к мёртвому .a элементу просто возвращает undefined.


Поменяйте код на:


const itemSelector = (id) => (state) => {
  console.log("run selector for ", id);
  switch (id) {
    case "a":
      return state.a.title;
    case "b":
      return state.b.title;
    case "c":
      return state.c.title;
    default:
      return state;
  }
};


В общем ровно та же самая ситуация. Просто ваш селектор написан не:


Обратите внимание на код селектора для ListItem — он специально написал "тупо", чтобы в случае наличия stale props возникла ошибка

а строго наоборот. Оттого что .a нет он ведь не упадёт. Просто вернёт undefined.

НЛО прилетело и опубликовало эту надпись здесь
но это не означает что сам компонент будет в дереве к тому моменту и получит stale props!

Да, его не будет в дереве. Всё дерево упадёт. Бинго :) Я вам об этом пишу уже 5-6 комментариев. Жаль вы их не читаете.


В статьях что вы привели выше указано как с этим пытается бороться react-redux — он отлавливает такие ошибки и даёт 2-й шанс. Но это не решает проблему полностью. Поэтому там есть рекомендация — писать селекторы так, чтобы они не падали. Либо не использовать хук useSelector.


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


НАЧНИТЕ УЖЕ НАКОНЕЦ ЧИТАТЬ ЧТО ВАМ ПИШУТ. Я С САМОГО НАЧАЛА ВАМ ПИШУ ОДНО И ТО ЖЕ.


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


вспоминаем что селекторы вызываются для получения понимания какие компоненты нужно обновить

Зачем вспоминать. Вы просто почитайте мои комментарии в этой теме. Я об этом вам и пишу. С самого начала. Вы ведь просто не читаете. У вас времени нет, да?

By the way. Я воссоздал точно такой же пример на react-redux и useSelector. Вот тут. Оно не падает. Ну это я итак ожидал. Но самое интересное вот:


reducer: delete b
<App/> render (2) ["a", "c"]
Unmount item id=b
Item's selector for a, st={a: {…}, c: {…}}
Item's selector for c, st={a: {…}, c: {…}}

  • оно не вызывает лишних Render-ов для a и для c (что ожидалось). Только их селекторы
  • оно не вызывает селектора для b. В итоге оно не падает даже в селекторе zombee

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


Что оказалось на самом деле. Там видимо используется useLayoutEffect как деструктор. Во всяком случае он вызывается ДО селекторов. Благодаря ему react-redux узнаёт о том, что ItemB уже не нужен и отменяет подписку. И даже не вызывает его селектор. use-context-selector всё делает сразу же в callback-е для createContext.


В общем интересно всё. Буду ковырять react-redux на досуге. Он не спроста написан в 5 раз сложнее, чем use-context-selector. Это даёт свои плоды.

Поковырял react-redux, понял как добиться вызова selector-а для потенциального zombee. Код. В прошлый раз оно не вызывалось ввиду того, что connect и useSelector работают по-разному. И получалось 2 последовательных render-а. Один позволил убрать лишние ноды. Второй уже не имел лишних подписок.


Но если в обоих местах задействовать useSelector, то та же проблема, что и в use-context-selection-е. Лишний selector вызывается за зря. И разумеется падает. Правда его подхватает код react-redux-а и просто помечает компонент на rerender. До rerener-а не доходит, т.к. порядок подписок правильный. В итоге ничего не падает и никаких ошибок в консоли.


Уверен это можно обмануть и всё таки уронить приложение. Но надо проявить больше фантазии. Ибо это просто грубый костыль.

НЛО прилетело и опубликовало эту надпись здесь
на базе use-context-selection можно строить эффективные решения в redux архитектуре, что и продемонстрировано на базе библиотеки @budarin/use-react-redux

Эффективные? Вы прикалываетесь что ли.

под капотом его активно использует Redux

И что? Redux же ущербное решение, чему тут удивляться то. Впрочем как и любое средство реактовское для управление состоянием.

я спорит с тупой мантрой, периодически встречающейся на просторах интернета, о том что Context не может быть полноценных хранилищем состояния

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

Неужели кому-то до сих пор не понятно, что React это не про управление состоянием приложения, React — это только view слой и всё. Инструменты для управления состоянием которые он предлагают настолько ущербные, что не пригодны к использованию. Поэтому для управления состоянием и используют MobX, а не React, Redux и прочую ересь.
фух… вот и все!

Прекрасно, давайте я подведу итоги спора:


  • AZaz1 не знает как работает React render. Но это не важно, зачем знать о чём споришь. Можно просто спорить.
  • AZaz1 не знает как работает use-context-selection. А зачем знать как работает решение, которое преподносится как серебряная пуля?
  • AZaz1 не смог написать демку правильно. Точнее в комментарии смог, а в коде нет
  • AZaz1 до сих пор думает что use-context-selection не имеет ряда проблем, которые здесь детально описаны.
  • AZaz1 не открывает демки, которые сделали персонально ему, ссылаясь на нехватку времени
  • AZaz1 игнорирует тот факт, что его собственная демка написана с ошибкой и, будучи, переписанной правильно, противоречит его же собственной позиции. Ссылается на нехватку времени
  • AZaz1 не умеет признавать ошибок, и даже не пытается. Всегда можно сказать, что нет времени
  • AZaz1 не вникает в то что ему пишут. Ведь у него нет времени

У вас, наверное, очень высокое ЧСВ :) Я бы уже наверное со стыда помер. А вы держитесь.

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
forseUpdate в данном случае это setState компонента использующего данный хук а не рефреш компонента

А разница-то в чём?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории