Reselect не так прост как кажется!

  Рет қаралды 12,972

АйТи Синяк

АйТи Синяк

Күн бұрын

Reselect кажется крайне простой библиотекой. Можно сказать практически одна функция createSelector. Как думаете вы хорошо понимаете, когда стоит использовать createSelector, а когда не стоит? Вы уверены?
Исходники бенчмарков - github.com/Sin9k/reselect-ben...
Поддержать Айти Синяка можно здесь:
KZbin: / @it-sin9k
boosty: boosty.to/sin9k
Patreon: / itsin9k
00:00 Анонс темы
00:47 Изучаем документацию reselect
02:34 Измеряем пример из документации
03:53 Изучаем первую и последующие итерации
07:00 Измеряем пример без reselect
08:50 Пример с 2-мя функциями createSelectors
10:08 Разбираем пример с 2-мя функциями createSelectors
12:40 Домашнее задание
13:35 Сравним бенчмарки 2 items и 10 items
15:05 Подписывайтесь
Подписаться на канал: / @it-sin9k
Twitter: / it_sin9k
________________
Канал о Фронтенде, который хочется порекомендовать (telegram):
t.me/frontendnoteschannel
-------------------------
Данный канал создан для инициирования бесед на различные темы IT сферы (социальные / технические), а также для тех кому короткая видео выжимка статьи, выступления на конференции или же просто личных мыслей, являются более удобным форматом

Пікірлер: 98
@nexus7172
@nexus7172 2 жыл бұрын
Не угадал, но если подумать, то все становится очевидно. Мемоизированный selectSubtotal вызывается дважды: из selectTotal и selectTax, т.е. время на чтение данных из памяти нужно умножать на 2, это будет уже 1.72 секунды. Т.к. теперь каждую итерацию нужно считать значения для двух селекторов (selectTotal и selectTax), то и временные расходы на это будут больше, чем на расчет значения лишь одного значения для селектора selectTotal. Если на подсчет значения селектора selectTotal уходит 0.28 секунды, то 0.13 секунды уходит на подсчет значения селектора selectTax.
@it-sin9k
@it-sin9k 2 жыл бұрын
Закрепил :)
@MDFireX5
@MDFireX5 7 ай бұрын
фиговое обьяснение.
@astkh4381
@astkh4381 Ай бұрын
@@MDFireX5 ну так объясни лучше.
@prog-hak
@prog-hak 2 жыл бұрын
опять шик блеск, глубоко и с разумом, спасибо 👍
@zafarkhamidullaev2067
@zafarkhamidullaev2067 2 жыл бұрын
Хотелось бы видео про микрофронтенды с использованием single-spa. До сих пор не могу найти годный контент про эту тему
@it-sin9k
@it-sin9k 2 жыл бұрын
Последние пол года пилю проект с использование ModuleFederation для микрофронтов) там действительно есть свои нюансы
@Catafey1
@Catafey1 2 жыл бұрын
Расскажи тогда про moduleFederation
@it-sin9k
@it-sin9k 2 жыл бұрын
@@Catafey1 Очередь из тем уже очень велика) сам иногда не знаю, какую следующую возьму тему)
@cikada3398
@cikada3398 2 жыл бұрын
@@it-sin9k бери moduleFederation :)
@it-sin9k
@it-sin9k 2 жыл бұрын
@@cikada3398 Надо за нее тоже взяться) но ближайшие три темы я уже запланировал)
@brightisrael1304
@brightisrael1304 2 жыл бұрын
Спасибо за подробный обзор Reselect
@kostyakozlov5289
@kostyakozlov5289 2 жыл бұрын
браво, думал раскидаю все примеры. В итоге вынес много нового
@it-sin9k
@it-sin9k 2 жыл бұрын
Спасибо!
@yuryitikhonoff9631
@yuryitikhonoff9631 2 жыл бұрын
Спасибо за контент. Я вообще-то думал, что реселект нужен для оптимизации рендеров Реакта за счёт мемоизаиции селекторов, а не для кеширования вычислений. Но референс на доку снимает все вопросы )))
@it-sin9k
@it-sin9k 2 жыл бұрын
Значит не зря делал это видео)
@d_r_robot
@d_r_robot 2 жыл бұрын
Спасибо!
@levsonc
@levsonc 2 жыл бұрын
С мемоизацией такая интересная штука, что выполнение в миллионную долю секунды - это очень быстро. Погрешность во времени выполнении на таких значениях несущественна. Зато взяв за правило всё мемоизировать вы с меньшей вероятностью столкнётесь с ситуацией, что у вас тормозит приложение из-за неэффективной работы в каких-то случаях. Такая стратегия эффективна для промышленной разработки. Единственная причина не делать этого - писать меньше кода. И тут наиболее интересным представляется проект React Forget, который по идее возьмёт это на себя. Правда, в случае Редакса, возможно, нужна будет отдельная реализация или плагин.
@it-sin9k
@it-sin9k 2 жыл бұрын
Хороший комментарий, эти темы планирую затронуть в следующем видео про reselect :)
@user-ju3tw3vb3o
@user-ju3tw3vb3o Жыл бұрын
так легко все оказывается! я все понял, спасибо
@dimitro.cardellini
@dimitro.cardellini Жыл бұрын
Привет! ;) Все не совсем так, как рассказал АйТи-Синяк ... Весь подвох заключается в последнем селекторе selectTotal. Именно он испортил бенчмарк, и именно его надо было оставлять единственным мемоизированным. Итак, по порядку: const selectTotal = createSelector( selectSubtotal, selectTax, (subtotal, tax) => ({ total: subtotal + tax }) ) Обратите внимание, что собственно последний селектор возвращает не просто число, а возвращает объект. Если мы selectTotal переписываем без createSelector: const selectTotal = state => ({ total: selectSubtotal(state) + selectTax(state) }); то, каждый вызов этого селектора будет возвращать новый объект (новую ссылку). Кстати, если мы начинаем создавать 1 миллиард объектов, то мы нагрузим свой движок упражнениями по управлению памятью, а еще де-оптимизируем наш бенчмарк (в случае с мемоизацией там вообще могло дойти до возвращения константы после, скажем, 100 тыс. итерации). Ну, и далее очевидно, если у нас будет компонент подписанный на selectTotal, то он будет рендериться при каждом dispatch-е, даже если state не менялся (ага, dispatch вызывает подписчиков даже, если state не поменялся: github.com/reduxjs/redux/blob/master/src/createStore.ts#L257). Собственно, reselect не столько про производительность, сколько про мемоизацию ссылок и предотвращение лишних рендеров. Так, что createSelector очень даже надо использовать для селекторов, возвращающих ссылки.
@it-sin9k
@it-sin9k Жыл бұрын
В данном видео, я рассматривал reselect в вакуме. Чтобы показать людям, что он не так предсказуем как кажется. Видео о reselect в рамках React, будет еще отдельным видео. Где конечно обсудим лишние рендеры :)
@dimitro.cardellini
@dimitro.cardellini Жыл бұрын
@@it-sin9k как работает реселект очень хорошо показано -- єто бесспорно. Но, все же давать оценку, что он не так предсказуем, без понимания того, для чего он нужен -- это несколько ... поспешно. Так, что ждем новое видео )
@user-cc4jp1xl6w
@user-cc4jp1xl6w 2 жыл бұрын
Я после этого выпуска прослезился и таки подписался на патреон)
@it-sin9k
@it-sin9k 2 жыл бұрын
Спасибо за подписку!!!
@d0paminer
@d0paminer 2 жыл бұрын
кажется, в реакт приложении это всё таки имеет смысл: из redux доки: "when an action is dispatched to the Redux store, useSelector() only forces a re-render if the selector result appears to be different than the last result. The default comparison is a strict === reference comparison. With useSelector(), returning a new object every time will always force a re-render by default... Call useSelector() multiple times, with each call returning a single field value If you want to retrieve multiple values from the store, you can Use Reselect or a similar library to create a memoized selector that returns multiple values in one object, but only returns a new object when one of the values has changed. Пример с 99999999 пересчетами не актуален в веб-приложениях. А перерендеры - это актуально.
@it-sin9k
@it-sin9k 2 жыл бұрын
Про рендеры и т.д. будет в следующей части реселкта :) а по поводу работы useSelector, мы это уже рассмотрели в предыдущих видео: kzbin.info/www/bejne/iYeqXqtjaaeEn8U
@Ramosok
@Ramosok 2 жыл бұрын
Super!
@kawaikaino5277
@kawaikaino5277 2 жыл бұрын
Использую реселект, только когда компонент использует UseSelector - в таком случае удается не перерендеривать компонент использующий UseSelector
@S.O..K.
@S.O..K. 11 ай бұрын
Я думаю, что дорогой является не взятие мемоизированного значения и не вычисление, а операция сравнения для того чтоб понять надо ли брать мемоизированное значение или надо проводить вычисление. Тоесть если взять третий пример (где selectTotal) не мемоизирован, то операция сравнения входных данных для проверки совпадают ли они с мемоизированным входом забрали львиную часть времени на вычисления...
@it-sin9k
@it-sin9k 11 ай бұрын
не очень уловил идею. Сравнение же идет по простому тройному ровно. Как это может быть дорого?
@boldureans
@boldureans 2 жыл бұрын
Я тоже не угадал) Спасибо за видео, Саш. Можешь снять про recoilJS? Она еще находится у меты под тегом экспериментального см, но выглядит уж очень любопытно 👀
@it-sin9k
@it-sin9k 2 жыл бұрын
Да, у меня список на изучение стоит recoil, zustand, effector)) Только обозревать все это не так просто и быстро)
@boldureans
@boldureans 2 жыл бұрын
@@it-sin9k понимаю) мало того, что нужно оформить кейс, еще и посмотреть как это устроено внутри) zustand самый мемный как по мне)
@boldureans
@boldureans 2 жыл бұрын
@@it-sin9k но recoil вроде как прям очевидный, у них классное видео на превью странице, которое четко описывает задачу.
@it-sin9k
@it-sin9k 2 жыл бұрын
Самое интересное, что мне про каждую из технологий, так кто то пишет) что одна технология это такое) а вот другая, намного интереснее)
@boldureans
@boldureans 2 жыл бұрын
@@it-sin9k интереснее не значит эффективная) каждая решает какие-то свои задачи, а превью вроде как всегда показывает самые лучшие юз кейсы без случаев когда НЕ стоит её использовать как мы узнали из сегодняшнего видео)
@AlefersXaoC
@AlefersXaoC Жыл бұрын
Привет. У меня вопрос, до ответа на который у меня разобраться не получается. У меня есть переменая, значения для которой я принимаю из createSelector передавая туда несколько других значений. Например это функция перевода, обёрнутая в createSelector, которая получает на вход объект фраз, ключи пути, фразу и объект переменных для вставки через замеру в результирующую строку. Теперь во всех местах где используется перевод я использую получается мемоизацию... но в упор не понимаю где хранится предыдущее состония вычисления и сама функция, если при просчёте компонента внутри переменной уже строковое значение. const translate = createSelector( парамсы => код и возврат результата) const phrase = translate(парамсы) - при это фраза это строка и в себе функцию не содержит, где тогда хранится предыдущее вычисленное значение?
@kacetal
@kacetal Жыл бұрын
Очень нравятся ваши видео, но если честно пример не очень корректный. Вы ведь не на бэкенде используете реселект с миллиардом операций. Думаю потеря времени на ререндер будет гораздо существенней чем экономя времени на кеширования реселект. Ps: не уверен что правильно именно так делать бенчмарк, разве у js нет таких понятий как прогрев? Или компиляция часто используемых функций в машинный код, инлайнинг итд.
@it-sin9k
@it-sin9k Жыл бұрын
Все верно про рендер. В рамках этого видео, хотел показать, что мемоизация не бесплатная на реальных цифрах. Да и показать, что иногда кажется, очевидно как работает инструмент, а на самом деле все немного сложнее. Чтобы показать всю историю с рендерами, я записал еще одно видео, показывающее какую именно проблему решает реселект: kzbin.info/www/bejne/qpPJoGVuhpqYl8k И собственно сейчас готовлю видео, про сам реселект в рамках React По поводу измерений, было много комментариев на эту тему, поэтому записал еще одно видео с более точными бенчмарками: kzbin.info/www/bejne/kIe2e5ahnsd-m5o
@kyzinatra6391
@kyzinatra6391 Жыл бұрын
А почему нельзя просто в реакте производить нужные вычисления из под useMemo к примеру. Тогда вы будем использовать только простые селекторые и в них самих ничего делать не будем. Следовательно не будет лишних вызовов из редукса. А лишние пересчеты самого реакта мы отметаем либо useMemo либо ключами при list rendering
@it-sin9k
@it-sin9k Жыл бұрын
Вы все правильно говорите, так и стоит поступать. Есть лишь один момент с селекторами. Иногда нужно рассчитать итоговую сумму товаров, с учетом карзины, скидок и т.д.. И эту итоговую сумму нужно показать в нескольких местах, абсолютно разных компонентах. В таком случае проще держать эту информацию в селекторах и делиться ей между компонентами
@MrMultiCrafting
@MrMultiCrafting 2 жыл бұрын
Представлена не совсем корректная схема работы селектора. Когда селектор вызывается повторно то он начинает процедуру сравнения данных из входящих селекторов и тем самым запрашивает данные у них триггеря туже процедуру. В итоге когда повторно вызывается selectTotal по прежнему вызываются и все остальные селекторы. Поэтому возникает такая разница между 3 и 0 селекторов, на проверку данных тратиться больше времени чем на их вычисление. Но когда количество итемов увеличилось до 10 то вычисления стали занимать больше времени чем их проверка и результат конкретно изменился при отсутствии селекторов. А при наличии селекторов время не изменилось так как айтемы считались один раз, а всё остальное время работали одинаковые проверки на схожесть входных данных и разница одного вычисления между 2 и 10 айтемами незначительна.
@MrMultiCrafting
@MrMultiCrafting 2 жыл бұрын
Также замечу что если селектор создаёт объект или массив то возврат замемоизированного значения сыграет в плюс так как лишит лишних ререндеров из-за того что useSelector будет возвращать хоть и такой же но новый объект/массив. Поэтому пример из документации будет работать производительнее так как он в конце возвращает замемоизированный объект и не будет ререндерить компонент))
@MrMultiCrafting
@MrMultiCrafting 2 жыл бұрын
Также вот пару основных признаков что вам нужно обернуть в createSelector: 1) У вас в селекторе производиться большое количество вычислений(много слаживаете, перемножаете, делите, объединяете строки) 2) Если у вас в селекторе есть цикл то в 99.99% случаев он делает больше действий чем проверка входных данных и мемоизация будет благом 3) если селектор возвращает массив или объект который был создан в теле селектора. Зачастую там будут возвращаться одинаковые но идентичные сущности.
@MrMultiCrafting
@MrMultiCrafting 2 жыл бұрын
И лайфхак в догонку: Если у вас в селекторе есть что-то типа такого: return myList || []; Или такого: return myObj || {}; То лучше вместо того чтобы создавать пустые сущности положите в константы EMPTY_ARR и EMPTY_OBJ и возвращайте их. Это может спасти вас от кучи лишних пересчётов селекторов и ререндеров.
@RaLLy869
@RaLLy869 2 жыл бұрын
@@MrMultiCraftingа как селектор понимает что данные не изменились получеется каждый раз проводит глубокое сравнивение?, а это ведь ресурсоемко.
@MrMultiCrafting
@MrMultiCrafting 2 жыл бұрын
@@RaLLy869 нет, он сравнивает их по тождественности. Поэтому и нужно мемоизировать возвращаемые объекты и массивы чтобы другие селекторы не перевыполнялись
@valentynlugovyi4789
@valentynlugovyi4789 2 жыл бұрын
Wtf, а что насчёт лишних ререндоров, я так понял их не будет но только если мы будет возвращать наружу значение которое было взято из createSelector. Эта тема не была поднята в видео но я считаю что это важно.
@it-sin9k
@it-sin9k 2 жыл бұрын
Все верно :) Про это будет отдельное видео
@valentynlugovyi4789
@valentynlugovyi4789 2 жыл бұрын
Кстати есть также возможность добавить колбек снаружи в useSelector-e вторым параметром, разве это тоже не своего рода мемоизация? Тот колбек должен отвечать за перерисовку компоненты... А видео больше про оптимизацию селекторов... Хотя тот колбек своего рода тоже оптимизация)) Интересно мнение автора.
@it-sin9k
@it-sin9k 2 жыл бұрын
@@valentynlugovyi4789 Про useSelector у нас есть отдельное видео, где как раз упоминается второй параметр (kzbin.info/www/bejne/iYeqXqtjaaeEn8U). По факту вы правы, этот колбек нужен именно для упразднения лишних рендеров :)
@armensargsyan8981
@armensargsyan8981 Жыл бұрын
у меня только один вопрос, не как не могу понять зачем использовать что то не по назначению, а патом жаловатся на проблемы?) useSelector (СЛОВО selector) не филр, не мутатор, не что то еще а именно selector... что касается удобства, в чем проблема отдельно отфилтровать? или создать хук в катором это делать? (это косательно реакт)
@it-sin9k
@it-sin9k Жыл бұрын
зачаствую нам нужен вычисленный селектор в многих местах. Например totalPrice. Не хочется ее вычислять несколько раз в разных местах. Куда удобнее посчитать 1 раз и в остальных местах отдавать значение из кэша
@armensargsyan8981
@armensargsyan8981 Жыл бұрын
@@it-sin9k а что мешает в initialState добавить totalPrice? и в редюсере при добавлении в массив price новой цены обновить totalPrice, потому как примая зависимость totalPrice prices
@it-sin9k
@it-sin9k Жыл бұрын
​@@armensargsyan8981 От данного подхода давно отказались. У него есть 2 проблемы: - totalPrice может высчитываться из допустим 5-6 параметров (цены, скидки, время суток и т.д.). В итоге на любой из экшенов обновления любого из полей надо обновлять и totalPrice. А если еще какой-то из полей надо хранить в каком то другом редьюсере, то нужно в экшен еще пробрасывать значение всех нужных полей. В итоге увеличивается шанс, что totalPrice будет не настоящий. Поэтому все и используют вычисляемый селектор, вместо хранения. Чтобы избежать нескольких источников истины и их синхронизации - 2-ая проблема, это получится, что в reducer будет хранится какая то бизнес логика. А от этого давно вроде все решили избавляться. Не местой бизнес логике в редьюсере
@armensargsyan8981
@armensargsyan8981 Жыл бұрын
@@it-sin9k можно все это обойти, во первых вынести totalPrice в отдельный reducer (конечно ломается логическая взоимосвяз между prices totalPrice что плохо, но думаю приемлимо, так как он зависит еще и от 5-6 полей которые в других редюсерах), и что то новое но все же(использовать некий вычислительный компонент(watcher) который нечего не рендерит а только диспатчит totalPrice при изменении 5-6 пропсов): по итогу вычисляется только единожды, и все работает как задумано. НО огромное спасибо!!! за обяснение что как, понял смысл селекторов и createSelector-а, еще раз спасибо!
@pluto2656
@pluto2656 Жыл бұрын
Вердикт: забыть вместе с redux
@user-ce9ff2rj4o
@user-ce9ff2rj4o 2 жыл бұрын
Этот бенчмарк не отражает действительности из-за оптимизаций v8
@it-sin9k
@it-sin9k 2 жыл бұрын
Какой-то конкретной оптимизации?
@grandphone3585
@grandphone3585 2 жыл бұрын
@@it-sin9k kzbin.info/www/bejne/foGpcoWfq619iLs Хорошее видео про оптимизацию и бенчмарки
@it-sin9k
@it-sin9k 2 жыл бұрын
@@grandphone3585 да, я знаком с подобного рода видео. Они очень поучительны. И конечно я не могу гарантировать, что мои бенчмарки правдивы. Но и говорить, что именно эти бенчмарки не отражают действительность мы тоже не можем. Далее напрашивается вопрос, а почему же я в итоге решил записать подобного рода видео. Основной мотивацией служили косвенные признаки. Я делал разного рода бенчмарки, и общая динамика отражала, идеи которые заложены в этом коде. Да действительно возможно 99_999_999 выполняются за иное количество времени, чем у меня в видео, но мне тут были важны не абсолютные значения, а скорее общая динамика (быстрее или медленее код выполняется). Поэтому для меня это было убедительным доводом, чтобы записать такого рода видео
@user-ce9ff2rj4o
@user-ce9ff2rj4o 2 жыл бұрын
@@it-sin9k я не спорю с относительными цифрами. Просто в видео подчеркивается, что все выполняется очень быстро хотя так как функций не имеют сайд эффектов, то v8 их выполнил только несколько раз. А в случае с функциями без мемоизации у него хуже вышло В целом из ролика следует, что преждевременной оптимизацией заниматься не стоит
@user-jh2id4qf5w
@user-jh2id4qf5w 2 жыл бұрын
А добавь плиз eval(' ') в свои функции, чтобы наверняка отключить оптимизации, и покажи новые числа.
@mr.nikita
@mr.nikita 2 жыл бұрын
Данная библиотек необходима для предотвращения лишних ререндоров.
@kawaikaino5277
@kawaikaino5277 2 жыл бұрын
я тоже ее только для этого использую... Т.к при использовании голого UseSelector компонент всегда ререндерится
@RaLLy869
@RaLLy869 2 жыл бұрын
Если что в useSelector можно предотвратить ререндер сравнением стейтов
@mr.nikita
@mr.nikita 2 жыл бұрын
@@RaLLy869, немного не понял. Хотелось бы увидеть example
@RaLLy869
@RaLLy869 2 жыл бұрын
@@mr.nikita прочитай в официальной доке про 2 аргумент хука, "equalityFn"
@mr.nikita
@mr.nikita 2 жыл бұрын
@@RaLLy869 Спасибо. Не знал об этом
@redgreengrey
@redgreengrey 2 жыл бұрын
не угадал
@hihoho1578
@hihoho1578 2 жыл бұрын
когда уже эта поделка (редакс) уступит место чему-то более адекватному реальности?)
@it-sin9k
@it-sin9k 2 жыл бұрын
например чему?)
@hihoho1578
@hihoho1578 2 жыл бұрын
​@@it-sin9k использую эффектор 3+ года в проде, полёт отличный. жаль что он только в узких кругах знаком и в ещё более узких используется)
@it-sin9k
@it-sin9k 2 жыл бұрын
@@hihoho1578 Я бы не сказал что уже в узких, много людей писали про него уже мне) поэтому планирую обратить внимание на него)
@hihoho1578
@hihoho1578 2 жыл бұрын
@@it-sin9k ждём обзор)
@levsonc
@levsonc 2 жыл бұрын
@@hihoho1578 Ну вот Ситнику настолько не понравились отдельные проблемы эффектора (практически нельзя уменьшить размер), что даже написал свой Наностор.
@NoName-zh7cc
@NoName-zh7cc 2 жыл бұрын
Лучшие видосы по реакту на русском языке
@it-sin9k
@it-sin9k 2 жыл бұрын
Спасибо! Мы очень стараемся!)
@RaLLy869
@RaLLy869 2 жыл бұрын
вместо new Date лучше использовать performance.now()
@it-sin9k
@it-sin9k 2 жыл бұрын
А оно в ноде то ли не работало, то ли еще что-то. Я уже не помню точно)
@RaLLy869
@RaLLy869 2 жыл бұрын
@@it-sin9k он в ноде под perf_hooks.performance находится
@it-sin9k
@it-sin9k 2 жыл бұрын
@@RaLLy869 попробую обязательно)
React Batching от создания (v0.4.0) до React 18
36:20
АйТи Синяк
Рет қаралды 18 М.
تجربة أغرب توصيلة شحن ضد القطع تماما
00:56
صدام العزي
Рет қаралды 57 МЛН
路飞被小孩吓到了#海贼王#路飞
00:41
路飞与唐舞桐
Рет қаралды 76 МЛН
Чего ожидать от HTTP/3 + Go
51:07
Нина Пакшина
Рет қаралды 3,1 М.
React придумал новый компонент!
8:28
АйТи Синяк
Рет қаралды 50 М.
ЭТИ КНИГИ СДЕЛАЮТ ИЗ ТЕБЯ ХАКЕРА
16:38
React Reconciliation
11:53
АйТи Синяк
Рет қаралды 86 М.
تجربة أغرب توصيلة شحن ضد القطع تماما
00:56
صدام العزي
Рет қаралды 57 МЛН