Не угадал, но если подумать, то все становится очевидно. Мемоизированный selectSubtotal вызывается дважды: из selectTotal и selectTax, т.е. время на чтение данных из памяти нужно умножать на 2, это будет уже 1.72 секунды. Т.к. теперь каждую итерацию нужно считать значения для двух селекторов (selectTotal и selectTax), то и временные расходы на это будут больше, чем на расчет значения лишь одного значения для селектора selectTotal. Если на подсчет значения селектора selectTotal уходит 0.28 секунды, то 0.13 секунды уходит на подсчет значения селектора selectTax.
@it-sin9k2 жыл бұрын
Закрепил :)
@MDFireX511 ай бұрын
фиговое обьяснение.
@astkh43815 ай бұрын
@@MDFireX5 ну так объясни лучше.
@zafarkhamidullaev20672 жыл бұрын
Хотелось бы видео про микрофронтенды с использованием single-spa. До сих пор не могу найти годный контент про эту тему
@it-sin9k2 жыл бұрын
Последние пол года пилю проект с использование ModuleFederation для микрофронтов) там действительно есть свои нюансы
@Catafey12 жыл бұрын
Расскажи тогда про moduleFederation
@it-sin9k2 жыл бұрын
@@Catafey1 Очередь из тем уже очень велика) сам иногда не знаю, какую следующую возьму тему)
@cikada33982 жыл бұрын
@@it-sin9k бери moduleFederation :)
@it-sin9k2 жыл бұрын
@@cikada3398 Надо за нее тоже взяться) но ближайшие три темы я уже запланировал)
@ТалгатМуканов-б5ж Жыл бұрын
так легко все оказывается! я все понял, спасибо
@kostyakozlov52892 жыл бұрын
браво, думал раскидаю все примеры. В итоге вынес много нового
@it-sin9k2 жыл бұрын
Спасибо!
@AlefersXaoC Жыл бұрын
Привет. У меня вопрос, до ответа на который у меня разобраться не получается. У меня есть переменая, значения для которой я принимаю из createSelector передавая туда несколько других значений. Например это функция перевода, обёрнутая в createSelector, которая получает на вход объект фраз, ключи пути, фразу и объект переменных для вставки через замеру в результирующую строку. Теперь во всех местах где используется перевод я использую получается мемоизацию... но в упор не понимаю где хранится предыдущее состония вычисления и сама функция, если при просчёте компонента внутри переменной уже строковое значение. const translate = createSelector( парамсы => код и возврат результата) const phrase = translate(парамсы) - при это фраза это строка и в себе функцию не содержит, где тогда хранится предыдущее вычисленное значение?
@prog-hak2 жыл бұрын
опять шик блеск, глубоко и с разумом, спасибо 👍
@levsonc2 жыл бұрын
С мемоизацией такая интересная штука, что выполнение в миллионную долю секунды - это очень быстро. Погрешность во времени выполнении на таких значениях несущественна. Зато взяв за правило всё мемоизировать вы с меньшей вероятностью столкнётесь с ситуацией, что у вас тормозит приложение из-за неэффективной работы в каких-то случаях. Такая стратегия эффективна для промышленной разработки. Единственная причина не делать этого - писать меньше кода. И тут наиболее интересным представляется проект React Forget, который по идее возьмёт это на себя. Правда, в случае Редакса, возможно, нужна будет отдельная реализация или плагин.
@it-sin9k2 жыл бұрын
Хороший комментарий, эти темы планирую затронуть в следующем видео про reselect :)
@brightisrael13042 жыл бұрын
Спасибо за подробный обзор Reselect
@S.O..K. Жыл бұрын
Я думаю, что дорогой является не взятие мемоизированного значения и не вычисление, а операция сравнения для того чтоб понять надо ли брать мемоизированное значение или надо проводить вычисление. Тоесть если взять третий пример (где selectTotal) не мемоизирован, то операция сравнения входных данных для проверки совпадают ли они с мемоизированным входом забрали львиную часть времени на вычисления...
@it-sin9k Жыл бұрын
не очень уловил идею. Сравнение же идет по простому тройному ровно. Как это может быть дорого?
@Дмитрий-ч3д9ю2 жыл бұрын
Я после этого выпуска прослезился и таки подписался на патреон)
@it-sin9k2 жыл бұрын
Спасибо за подписку!!!
@dimitro.cardellini2 жыл бұрын
Привет! ;) Все не совсем так, как рассказал АйТи-Синяк ... Весь подвох заключается в последнем селекторе 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-sin9k2 жыл бұрын
В данном видео, я рассматривал reselect в вакуме. Чтобы показать людям, что он не так предсказуем как кажется. Видео о reselect в рамках React, будет еще отдельным видео. Где конечно обсудим лишние рендеры :)
@dimitro.cardellini2 жыл бұрын
@@it-sin9k как работает реселект очень хорошо показано -- єто бесспорно. Но, все же давать оценку, что он не так предсказуем, без понимания того, для чего он нужен -- это несколько ... поспешно. Так, что ждем новое видео )
@kawaikaino52772 жыл бұрын
Использую реселект, только когда компонент использует UseSelector - в таком случае удается не перерендеривать компонент использующий UseSelector
@d0paminer2 жыл бұрын
кажется, в реакт приложении это всё таки имеет смысл: из 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-sin9k2 жыл бұрын
Про рендеры и т.д. будет в следующей части реселкта :) а по поводу работы useSelector, мы это уже рассмотрели в предыдущих видео: kzbin.info/www/bejne/iYeqXqtjaaeEn8U
@yuryitikhonoff96312 жыл бұрын
Спасибо за контент. Я вообще-то думал, что реселект нужен для оптимизации рендеров Реакта за счёт мемоизаиции селекторов, а не для кеширования вычислений. Но референс на доку снимает все вопросы )))
@it-sin9k2 жыл бұрын
Значит не зря делал это видео)
@kyzinatra6391 Жыл бұрын
А почему нельзя просто в реакте производить нужные вычисления из под useMemo к примеру. Тогда вы будем использовать только простые селекторые и в них самих ничего делать не будем. Следовательно не будет лишних вызовов из редукса. А лишние пересчеты самого реакта мы отметаем либо useMemo либо ключами при list rendering
@it-sin9k Жыл бұрын
Вы все правильно говорите, так и стоит поступать. Есть лишь один момент с селекторами. Иногда нужно рассчитать итоговую сумму товаров, с учетом карзины, скидок и т.д.. И эту итоговую сумму нужно показать в нескольких местах, абсолютно разных компонентах. В таком случае проще держать эту информацию в селекторах и делиться ей между компонентами
@MrMultiCrafting2 жыл бұрын
Представлена не совсем корректная схема работы селектора. Когда селектор вызывается повторно то он начинает процедуру сравнения данных из входящих селекторов и тем самым запрашивает данные у них триггеря туже процедуру. В итоге когда повторно вызывается selectTotal по прежнему вызываются и все остальные селекторы. Поэтому возникает такая разница между 3 и 0 селекторов, на проверку данных тратиться больше времени чем на их вычисление. Но когда количество итемов увеличилось до 10 то вычисления стали занимать больше времени чем их проверка и результат конкретно изменился при отсутствии селекторов. А при наличии селекторов время не изменилось так как айтемы считались один раз, а всё остальное время работали одинаковые проверки на схожесть входных данных и разница одного вычисления между 2 и 10 айтемами незначительна.
@MrMultiCrafting2 жыл бұрын
Также замечу что если селектор создаёт объект или массив то возврат замемоизированного значения сыграет в плюс так как лишит лишних ререндеров из-за того что useSelector будет возвращать хоть и такой же но новый объект/массив. Поэтому пример из документации будет работать производительнее так как он в конце возвращает замемоизированный объект и не будет ререндерить компонент))
@MrMultiCrafting2 жыл бұрын
Также вот пару основных признаков что вам нужно обернуть в createSelector: 1) У вас в селекторе производиться большое количество вычислений(много слаживаете, перемножаете, делите, объединяете строки) 2) Если у вас в селекторе есть цикл то в 99.99% случаев он делает больше действий чем проверка входных данных и мемоизация будет благом 3) если селектор возвращает массив или объект который был создан в теле селектора. Зачастую там будут возвращаться одинаковые но идентичные сущности.
@MrMultiCrafting2 жыл бұрын
И лайфхак в догонку: Если у вас в селекторе есть что-то типа такого: return myList || []; Или такого: return myObj || {}; То лучше вместо того чтобы создавать пустые сущности положите в константы EMPTY_ARR и EMPTY_OBJ и возвращайте их. Это может спасти вас от кучи лишних пересчётов селекторов и ререндеров.
@azad08082 жыл бұрын
@@MrMultiCraftingа как селектор понимает что данные не изменились получеется каждый раз проводит глубокое сравнивение?, а это ведь ресурсоемко.
@MrMultiCrafting2 жыл бұрын
@@azad0808 нет, он сравнивает их по тождественности. Поэтому и нужно мемоизировать возвращаемые объекты и массивы чтобы другие селекторы не перевыполнялись
@d_r_robot2 жыл бұрын
Спасибо!
@kacetal Жыл бұрын
Очень нравятся ваши видео, но если честно пример не очень корректный. Вы ведь не на бэкенде используете реселект с миллиардом операций. Думаю потеря времени на ререндер будет гораздо существенней чем экономя времени на кеширования реселект. Ps: не уверен что правильно именно так делать бенчмарк, разве у js нет таких понятий как прогрев? Или компиляция часто используемых функций в машинный код, инлайнинг итд.
@it-sin9k Жыл бұрын
Все верно про рендер. В рамках этого видео, хотел показать, что мемоизация не бесплатная на реальных цифрах. Да и показать, что иногда кажется, очевидно как работает инструмент, а на самом деле все немного сложнее. Чтобы показать всю историю с рендерами, я записал еще одно видео, показывающее какую именно проблему решает реселект: kzbin.info/www/bejne/qpPJoGVuhpqYl8k И собственно сейчас готовлю видео, про сам реселект в рамках React По поводу измерений, было много комментариев на эту тему, поэтому записал еще одно видео с более точными бенчмарками: kzbin.info/www/bejne/kIe2e5ahnsd-m5o
@armensargsyan8981 Жыл бұрын
у меня только один вопрос, не как не могу понять зачем использовать что то не по назначению, а патом жаловатся на проблемы?) useSelector (СЛОВО selector) не филр, не мутатор, не что то еще а именно selector... что касается удобства, в чем проблема отдельно отфилтровать? или создать хук в катором это делать? (это косательно реакт)
@it-sin9k Жыл бұрын
зачаствую нам нужен вычисленный селектор в многих местах. Например totalPrice. Не хочется ее вычислять несколько раз в разных местах. Куда удобнее посчитать 1 раз и в остальных местах отдавать значение из кэша
@armensargsyan8981 Жыл бұрын
@@it-sin9k а что мешает в initialState добавить totalPrice? и в редюсере при добавлении в массив price новой цены обновить totalPrice, потому как примая зависимость totalPrice prices
@it-sin9k Жыл бұрын
@@armensargsyan8981 От данного подхода давно отказались. У него есть 2 проблемы: - totalPrice может высчитываться из допустим 5-6 параметров (цены, скидки, время суток и т.д.). В итоге на любой из экшенов обновления любого из полей надо обновлять и totalPrice. А если еще какой-то из полей надо хранить в каком то другом редьюсере, то нужно в экшен еще пробрасывать значение всех нужных полей. В итоге увеличивается шанс, что totalPrice будет не настоящий. Поэтому все и используют вычисляемый селектор, вместо хранения. Чтобы избежать нескольких источников истины и их синхронизации - 2-ая проблема, это получится, что в reducer будет хранится какая то бизнес логика. А от этого давно вроде все решили избавляться. Не местой бизнес логике в редьюсере
@armensargsyan8981 Жыл бұрын
@@it-sin9k можно все это обойти, во первых вынести totalPrice в отдельный reducer (конечно ломается логическая взоимосвяз между prices totalPrice что плохо, но думаю приемлимо, так как он зависит еще и от 5-6 полей которые в других редюсерах), и что то новое но все же(использовать некий вычислительный компонент(watcher) который нечего не рендерит а только диспатчит totalPrice при изменении 5-6 пропсов): по итогу вычисляется только единожды, и все работает как задумано. НО огромное спасибо!!! за обяснение что как, понял смысл селекторов и createSelector-а, еще раз спасибо!
@pluto2656 Жыл бұрын
Вердикт: забыть вместе с redux
@Ramosok2 жыл бұрын
Super!
@valentynlugovyi47892 жыл бұрын
Wtf, а что насчёт лишних ререндоров, я так понял их не будет но только если мы будет возвращать наружу значение которое было взято из createSelector. Эта тема не была поднята в видео но я считаю что это важно.
@it-sin9k2 жыл бұрын
Все верно :) Про это будет отдельное видео
@valentynlugovyi47892 жыл бұрын
Кстати есть также возможность добавить колбек снаружи в useSelector-e вторым параметром, разве это тоже не своего рода мемоизация? Тот колбек должен отвечать за перерисовку компоненты... А видео больше про оптимизацию селекторов... Хотя тот колбек своего рода тоже оптимизация)) Интересно мнение автора.
@it-sin9k2 жыл бұрын
@@valentynlugovyi4789 Про useSelector у нас есть отдельное видео, где как раз упоминается второй параметр (kzbin.info/www/bejne/iYeqXqtjaaeEn8U). По факту вы правы, этот колбек нужен именно для упразднения лишних рендеров :)
@boldureans2 жыл бұрын
Я тоже не угадал) Спасибо за видео, Саш. Можешь снять про recoilJS? Она еще находится у меты под тегом экспериментального см, но выглядит уж очень любопытно 👀
@it-sin9k2 жыл бұрын
Да, у меня список на изучение стоит recoil, zustand, effector)) Только обозревать все это не так просто и быстро)
@boldureans2 жыл бұрын
@@it-sin9k понимаю) мало того, что нужно оформить кейс, еще и посмотреть как это устроено внутри) zustand самый мемный как по мне)
@boldureans2 жыл бұрын
@@it-sin9k но recoil вроде как прям очевидный, у них классное видео на превью странице, которое четко описывает задачу.
@it-sin9k2 жыл бұрын
Самое интересное, что мне про каждую из технологий, так кто то пишет) что одна технология это такое) а вот другая, намного интереснее)
@boldureans2 жыл бұрын
@@it-sin9k интереснее не значит эффективная) каждая решает какие-то свои задачи, а превью вроде как всегда показывает самые лучшие юз кейсы без случаев когда НЕ стоит её использовать как мы узнали из сегодняшнего видео)
@ВалерийСмирнов-у9ш2 жыл бұрын
Этот бенчмарк не отражает действительности из-за оптимизаций v8
@it-sin9k2 жыл бұрын
Какой-то конкретной оптимизации?
@grandphone35852 жыл бұрын
@@it-sin9k kzbin.info/www/bejne/foGpcoWfq619iLs Хорошее видео про оптимизацию и бенчмарки
@it-sin9k2 жыл бұрын
@@grandphone3585 да, я знаком с подобного рода видео. Они очень поучительны. И конечно я не могу гарантировать, что мои бенчмарки правдивы. Но и говорить, что именно эти бенчмарки не отражают действительность мы тоже не можем. Далее напрашивается вопрос, а почему же я в итоге решил записать подобного рода видео. Основной мотивацией служили косвенные признаки. Я делал разного рода бенчмарки, и общая динамика отражала, идеи которые заложены в этом коде. Да действительно возможно 99_999_999 выполняются за иное количество времени, чем у меня в видео, но мне тут были важны не абсолютные значения, а скорее общая динамика (быстрее или медленее код выполняется). Поэтому для меня это было убедительным доводом, чтобы записать такого рода видео
@ВалерийСмирнов-у9ш2 жыл бұрын
@@it-sin9k я не спорю с относительными цифрами. Просто в видео подчеркивается, что все выполняется очень быстро хотя так как функций не имеют сайд эффектов, то v8 их выполнил только несколько раз. А в случае с функциями без мемоизации у него хуже вышло В целом из ролика следует, что преждевременной оптимизацией заниматься не стоит
@ЮрийБондаренко-у7е2 жыл бұрын
А добавь плиз eval(' ') в свои функции, чтобы наверняка отключить оптимизации, и покажи новые числа.
@mr.nikita2 жыл бұрын
Данная библиотек необходима для предотвращения лишних ререндоров.
@kawaikaino52772 жыл бұрын
я тоже ее только для этого использую... Т.к при использовании голого UseSelector компонент всегда ререндерится
@azad08082 жыл бұрын
Если что в useSelector можно предотвратить ререндер сравнением стейтов
@mr.nikita2 жыл бұрын
@@azad0808, немного не понял. Хотелось бы увидеть example
@azad08082 жыл бұрын
@@mr.nikita прочитай в официальной доке про 2 аргумент хука, "equalityFn"
@mr.nikita2 жыл бұрын
@@azad0808 Спасибо. Не знал об этом
@redgreengrey2 жыл бұрын
не угадал
@NoName-zh7cc2 жыл бұрын
Лучшие видосы по реакту на русском языке
@it-sin9k2 жыл бұрын
Спасибо! Мы очень стараемся!)
@hihoho15782 жыл бұрын
когда уже эта поделка (редакс) уступит место чему-то более адекватному реальности?)
@it-sin9k2 жыл бұрын
например чему?)
@hihoho15782 жыл бұрын
@@it-sin9k использую эффектор 3+ года в проде, полёт отличный. жаль что он только в узких кругах знаком и в ещё более узких используется)
@it-sin9k2 жыл бұрын
@@hihoho1578 Я бы не сказал что уже в узких, много людей писали про него уже мне) поэтому планирую обратить внимание на него)
@hihoho15782 жыл бұрын
@@it-sin9k ждём обзор)
@levsonc2 жыл бұрын
@@hihoho1578 Ну вот Ситнику настолько не понравились отдельные проблемы эффектора (практически нельзя уменьшить размер), что даже написал свой Наностор.
@azad08082 жыл бұрын
вместо new Date лучше использовать performance.now()
@it-sin9k2 жыл бұрын
А оно в ноде то ли не работало, то ли еще что-то. Я уже не помню точно)
@azad08082 жыл бұрын
@@it-sin9k он в ноде под perf_hooks.performance находится