Комментарии 12
В приведенном коде хука все танцы с useCallback
бесмысленны, так как функция setSearchQuery
пересоздается каждый раз. А filter
никаким образом не мемоизируется, и будет триггерить пересоздание, так как это объект, а массив из dependencies
работает по принципу строгого сравнения.
Как результат, множество лишних рендеров и дополнительный оверхед в виде неработающей мемоизации.
Кстати, совершенно не обязательно добавлять функцию-сеттер из useState
в массив dependencies
у useMemo
и useCallback
.
P.S.: Комментарий написал исключительно, чтобы текущее решение не было скопировано в реальный проект один в один.
Спасибо за замечание, пожалуй с вами соглашусь, поправлю
Кстати, совершенно не обязательно добавлять функцию-сеттер из
useState
в массивdependencies
уuseMemo
иuseCallback
.
Лучше добавлять. Иначе линтер наругается, придется его накрывать дизаблом, и задизабленный линтер потом не поймает реальную проблему.
+1 про бесполезные useCallback. Дело в том что коллбек зависит от getFilterQuery, а его вполне могут передавать вот так
useFiltersQuery(filter => /* do something */)
В этой ситуации, все эти useCallback оказываются мартышкиным трудом, безо всякой пользы.
Более того, даже в идеальном случае useCallback не покажут никаких улучшений. @Roman9131 у вас есть бенчмарк показывающий пользу useCallback? А если нет, зачем вы чинили то что не сломано?
Дело в том что коллбек зависит от getFilterQuery, а его вполне могут передавать вот так
Понятное дело, что если где-то упоролись в useCallback, то надо проследить всю цепочку.
Ясное дело, что нужно проследить всю цепочку. Но это приводит к очень хрупкому коду. При этом выгода от всего этого совершенно не понятна
Не уверен, что это называется "хрупкий код". Жизнеспособность его не страдает, если где-то кто-то не поддержал мемоизацию. Это просто дополнительная опция, которая окажется полезной или нет, при этом никак не придется менять хук.
К тому же без useCallback могут быть не только лишние ререндеры, а ещё лишние срабатывания useEffect, что тоже так себе.
Спасибо за комментарий, useCallback и useMemo(немного поправил получение фильтра) здесь нужны, для того чтобы при каждом ререндере возвращать мемоизированные значения. Иначе, все дочерние компоненты, которые будут их получать в качестве пропсов, будут ререндериться каждый раз. Идея добавление useCallback, как раз была именно в том, чтобы исключить рередер дочерних компонентов. В случае передачи функций таким способом, как вы предложили, действительно будет создаваться каждый раз новая функция, но зачем так делать. Если если нужна оптимизация, то лучше передавать одну и туже ссылку на функцию, путем импортирования например из вне React компонента или мемоизируя ссылку внутри компонента.
В получившемся хуке есть известный антипаттерн "производный стейт". А именно, filter - ни что иное, как значение, которое зависит только от useLocation().search. Использовать это необходимо, только если предполагается менять состояние отдельно от источника: хук useState как бы отправляет его в "самостоятельное плавание". Иногда добавляют useEffect, если в некоторых случаях надо снова подхватить значение из источника или что-то подмержить. Хук в статье ничего этого не делает, просто искусственно поддерживает два одиинаковых значения в локальном стейте и источнике. То есть, как только кто-то "посторонний" поменяет search, то мы об этом не узнаем - ведь filter только при создании компонента берётся из истории.
Правильный подход - просто вычислять на каждом рендере filter из search (если эти вычисления кажутся тяжелыми, то useMemo в помощь). В обоих функциях, которые возвращаются из хука, менять только history (тогде и useCallback на них будет легко навесить).
И нейминг у этих функций поправить, назвать их changeFilter и clearFilter.
Пишем кастомный Хук для фильтров используя параметры страницы (query string)