Comments 44
Как мы боролись с лишними рендерами в react
С 2017 года начали использовать его в связке с MobX и эту проблему сразу как рукой сняло, по сей день. И не нужно заниматься этими извращениями превращающими код в месиво. React только для View. Управление состоянием, в том числе локальным компонента эта забота MobX'a, реакт до сих пор с ней отвратительно справляется.
Это каким таким волшебным образом mobx спасает вас от необходимости формировать и реконцилировать развесистый дом при добавлении нового элемента? А как он спасает вас от ререндеров из-за необходимости пересоздавать колбэки для обновления значений в них?
Ну вот же, нет никаких лишних ререндеров. Только те компоненты, которые изменяются и изменения в родителе не тригерят рендер дочерних.
Откройте консоль и понажимайте:
https://stackblitz.com/edit/stackblitz-starters-eowyumpg?file=src%2FApp.tsx
Чего не скажешь о голом реакте
https://stackblitz.com/edit/stackblitz-starters-6gcj5993?file=src%2FApp.tsx
Ну и соответственно никакие zustand'ы от этого тоже не спасают, все лишнее перерендеривается только в путь
https://stackblitz.com/edit/stackblitz-starters-zzn16f51?file=src%2FApp.tsx
На первый вопрос не ответил, а на второй прислал какую-то демку с состоянием в глобальной переменной и без пропсов.. вы кого пытаетесь обмануть?
На первый вопрос не ответил
Добавление новых элементов к делу(лишние перерендеры компонентов, которые не должны перерендериваться) не относится.
а на второй прислал какую-то демку с состоянием в глобальной переменной и без пропсов.. вы кого пытаетесь обмануть?
1) Пропсы к делу не относятся, т.к. при изменении пропса компонент и так должен перерендериваться, в этом весь смысл.
2) Там есть глобальный стейт и локальный. Тем более для React + MobX, нет разницы MobX стор подключен как глобальное состояние или локальное конкретного компонента.
3) Вот пробросил метод через прос в дочерние компоненты, чтобы они его вызывали и меняли состояние у компонента родителя:
https://stackblitz.com/edit/stackblitz-starters-k5jbjqqv?file=src%2FApp.tsx
Разумеется ничего не поменялось и никаких лишних рендеров не добавилось.
Смысл избавления от рендеров в том, чтобы выработать хороший перформанс. А ты строишь дерево из самой антиперфоманс функции реакта, и хамиш челу с мобиксом , которой , к слову приблизился к перфоимансу больше чем ты, и нормально с тобой разговаривал показав тебе примеры взаимодействия с контролем рендера. Приди в себя, токсичный ЛСник.
зато есть просто и мощный zustand, который набирает все больше и больше популярности с каждым днем. Я думаю за счет его простоты и легкого веса он станет основным инструментом стейт менеджмента, рано или поздно
зато есть просто и мощный zustand
Он вообще даже близко не стоит с MobX'ом по удобству использования, и в zustand много boilerplate кода надо писать, поменьше чем в redux конечно, но все равно много по сравнению с MobX, где его нет вообще. А ещё zustand иммутабильный и это тоже минус.
Я думаю за счет его простоты
Напишите для сравнения с mobx код, который будет создавать локальный стэйт. Посмотрите как создаётся локальный стэйт на zustand и на mobx.
Кстати ещё один жирный минус в анти копилку zustand'a:
Если в его стейте что-то изменяется, но при этом это не используется в компоненте который использует другие свойства из этого стейта, то компонент все равно каждый раз перерендеривается в пустую.

Вот ссылка:
https://stackblitz.com/edit/stackblitz-starters-ab7az7ym?file=src%2FApp.tsx
Не знаток zustand, но мне казалось, в useAppStore можно передать селектор..
Да, так можно) Но тогда код становится ещё заметно хуже и в нем ещё больше лишнего мусора.

И это всего лишь Hello World example, а не Real world. Но камон в реальной жизни во всех компонентах вот такие портянки раскрывать их могут быть десятки только в рамках одного компонента, а таких компонентов в проектах сотни. Это смешно. Зачем этот суицид если много много лет назад придумали выход, как сделать реакт идеальным, на сколько это вообще возможно.
Ну а зачем приводить пример из антипатернов использования и говорить, что оно плохо работает.
Вы приведите пример с best practises использования, и скажите, что оно плохо работает.
Не защищаю zustand, все же, хорошая либа должна по возможности не давать делать неправильные вещи и давать делать правильные с минимальными усилиями
Вы приведите пример с best practises использования, и скажите, что оно плохо работает.
Так best practises у zustanda === много лишнего грязного кода. Какой в этом смысл? Вам в кайф утопать в куче лишнего кода? В чем прикол сознательно одевать на себя пудовые ботинки чтобы бегать по набережной? Когда вот прям перед носом лежат отличные кроссовки весом в 80 грамм, с амортизирующей подошвой.
Ну а зачем приводить пример из антипатернов использования и говорить, что оно плохо работает.
Я специально привел пример, который применим именно для реальной жизни, чтобы хоть как-то приблизить zustand к удобному использованию и уменьшению месива из бестолкового лишнего кода.
Мне кажется, что это как раз тот случай, где нужно использовать redux, а лучше современный effector или там mobx. На худой конец накостылять свой глобальный стор на RxJS.
Если вы внезапно осознаёте себя в ситуации, где вы жонглируете детьми, чтобы динамически совать в них callback'и, то стоит остановиться, сесть и подумать, а как вы оказались в такой ситуации и как из неё выбираться. В 99% случаев вы пишите неподдерживаемое говно, которое будет в половине случаев разваливаться, а в другой половине вызывать ререндер всей страницы.
У нас в продукте используем redux. В данном случае все эти счётчики хранятся в слайсах каждого виджета. И цель была сообщить об этих счётчиках компоненту-меню.
Лезть из компонента-меню в слайсы каждого виджета - только не это.
Вынести эти счётчики в отдельный слайс - тоже как-то не очень, не понятно, кто владелец этого слайса, меню или виджеты.
Так что иногда и при живом redux приходится немного подумать, как лучше организовать взаимодействие.
У нас в продукте используем redux
И какой тогда вам смысл думать лишних рендерах и т.п? Когда берешь redux, то сознательно пишешь максимально плохой код, и с этим ничего нельзя поделать, так изначально задумано.
А теперь пытаетесь бороться с проблемами, которые сами себе специально и сознательно создали. Спрашивается зачем их было создавать?
Не до конца понял прямую связь redux и ререндеров. Можно без труда переписать код из статьи с использованием redux, и лишних ререндеров не будет вообще. Примерно так:
const Page = ({ children }) => {
const count = useSelector(state => ...);
const listItems = useMemo(() => {
return React.Children.map(children, c => {
const { id, title } = c.props;
return { id, title };
});
}, [children]);
return (
<>
<div>
{listItems.map(e => {
return (
<div key={e.id}>
{e.title}
{count[e.id]}
</div>
);
})}
</div>
<div>{children}</div>
</>
);
};
Из виджета диспатчим экшн:
const Widget = ({ id }) => {
...
const dispatch = useDispatch();
useEffect(() => {
dispatch({ type: "SET_COUNT", payload: { id, count: data.length } });
}, [dispatch, id, data]);
...
};
Так что redux не всегда так плох.
Но для решения нашей задачи redux не нужен, как и, возможно, не нужен какой-либо другой стейт менеджер.
Так что redux не всегда так плох.
Вы просто посмотрите на код, который вы в комментарии своем привели. И ещё раз подумайте)
как и, возможно, не нужен какой-либо другой стейт менеджер.
Нужен, ибо реакт ни в каком виде не способен обеспечить адекватное управление состоянием, из-за того же, что я указал ниже, единственный вариант это MobX.
Не до конца понял прямую связь redux и ререндеров
Без костылей как у вас, она прямая, ибо иммутабильность и отсутствие умного и незаметного для разработчика React.memo
А в чем именно проблема использования внутренних инструментов для состояния? Понятно, что у MobX свои преимущества есть. Но адекватно можно и на том, и на другом писать.
А в чем именно проблема использования внутренних инструментов для состояния?
1) Иммутабильность
- Это побеждается только использованием MobX
2) При перерендере родителя, перерендериваются дочерние компоненты, хотя не должны.
- Первый вариант(садо мазо) - это "лечится" заворачиванием всего и вся в ручную в React.memo
, useMemo
, useCallback
соответственно код становится очень грязным и вырвиглазным.
- Второй вариант(традиционный) - это лечится просто использованием MobX.
Но адекватно можно и на том, и на другом писать.
Используя внутренние инструменты реакта, адекватно можно написать только Hello World, причем в прямом смысле этого слова. Как только будет интерактив и изменение состояния, то безальтернативно реакт нужно использовать в связке с MobX. В противном случае сам выбор реакта ошибка и лучше тогда уж взять vue или svelte.
Я лютый приверженец MobX, но справедливости ради стоит отметить, что в нем тоже все , что читает observable поля должно быть завернуто в observer, у которого под капотом React.memo
Сложилось такое же впечатление...
А я не думаю, что стейт-менеджер тут на что-то влияет.
Что именно вы хотите решить, заменив useState на redux/MobX/ещё что-нибудь? Надо начать с первопричины.
Вся соль в том, что если менять значение атрибутов или, например, текстовое содержимое DOM-элемента, то возникнет его перерисовка, даже если новое значение эквивалентно старому.
Собственно, по этой причине используют всякие реактивные переменные и их аналоги, которые при получении нового значения понимают нужно ли что-то менять в DOM-элементе (путем соответствующей подписки на изменение значения переменной).
Да, отсутствие такой оптимизации может замедлять веб-интерфейс.
Однако, проблемы замедления интерфейса в большинстве случаев не будет вовсе при условии, что разработчик откажется использовать на странице десятки тысяч DOM-элементов одновременно.
Именно! Я об этом ниже писал
Если на странице есть скролл, то надо делать просто виртуальный скролл и не отображать лишение виджеты. И всё, проблема решена, число элементов на странице не стремится к бесконечности
Если виджеты образуют дерево как комментарии на Хабре, то виртуальный скролл вам ничем не поможет.
Это да, всё от задачи зависит
На картинке выглядит как будто просто линейный список, но в реальности всё, что угодно, может быть.
На самом деле не обязательно виртуальный скролл даже, можно же просто проверять, что виджет находится в пределах вью-порта и в зависимости от этого уже либо обновлять его данные, либо нет.
мы передаем каждому виджету дополнительный пропс-функцию, с помощью которой виджет сообщает значение своего счетчика.
useImperativeHandle и половину извращений можно выбрасывать
Все верно. В конце есть вариант с useImperativeHandle. Правда, useImperativeHandle само по себе ещё то извращение.
Зачем вообще так делать, не лучше просто сверху данные передавать?
Без всяких лишних useEffect-ов и useImperativeHandle
Данные наверху, отображение внизу. Данные о количестве уже имеются наверху, и никаких пропсов не нужно.
Если есть возможность написать без useEffect, значит нужно писать без useEffect, имхо.
Ещё эти cloneElement, children.map. На ровном месте проблемы себе создают, а потом их решают. С архитектурой явно что-то не то.
--------
Ну и самое важное: можно же не отображать данные, которые не видны на экране (вы говорите, что там скролл есть).
Соответственно, ваша архитектура уже не работает, т к у вас подписка на количество идёт из виджета (то есть скрыть не получается).
Это же самое простое, что можно сделать: просто не отображать, если не видно. Ну а данные вверху. Если данные рендерить не нужно, 90% проблем уходит сразу.
Данные наверху, отображение внизу. Данные о количестве уже имеются наверху, и никаких пропсов не нужно.
Данные о количестве не имеются наверху. Каждый виджет грузит свои данные. Поэтому данные поступают снизу. И нужно их пробросить наверх.
Или вы предлагается все данные централизовано загрузить где-то наверху?
Ещё эти cloneElement, children.map. На ровном месте проблемы себе создают, а потом их решают. С архитектурой явно что-то не то.
Ну мы не знаем сколько у нас виджетов же будет, у нас они постоянно добавляются и убираются, подстраиваясь под требования бизнеса. Удобно же, добавил виджет в качестве children, динамически получил пункт меню. Если у вас есть здравая идея на этот счет, делитесь.
Сложно сказать, так как много чего неизвестно: какие данные в виджетах, как они загружаются (централизованно через сервер, или виджеты вообще сами по себе живут отдельно).
Насколько произвольными могут быть эти виджеты, что у них общее (как вижу, количество данных - общее).
И как понять, постоянно убираются/добавляются. В общем, как и всегда, нужно больше информации, чтобы можно было подумать над решением)
Как мы боролись с лишними рендерами в react