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

Комментарии 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 компонента или мемоизируя ссылку внутри компонента.

Идея добавление useCallback, как раз была именно в том, чтобы исключить рередер дочерних компонентов

Чтобы что? Вы можете в реальных цифрах показать, что это улучшает?

В получившемся хуке есть известный антипаттерн "производный стейт". А именно, filter - ни что иное, как значение, которое зависит только от useLocation().search. Использовать это необходимо, только если предполагается менять состояние отдельно от источника: хук useState как бы отправляет его в "самостоятельное плавание". Иногда добавляют useEffect, если в некоторых случаях надо снова подхватить значение из источника или что-то подмержить. Хук в статье ничего этого не делает, просто искусственно поддерживает два одиинаковых значения в локальном стейте и источнике. То есть, как только кто-то "посторонний" поменяет search, то мы об этом не узнаем - ведь filter только при создании компонента берётся из истории.

Правильный подход - просто вычислять на каждом рендере filter из search (если эти вычисления кажутся тяжелыми, то useMemo в помощь). В обоих функциях, которые возвращаются из хука, менять только history (тогде и useCallback на них будет легко навесить).

И нейминг у этих функций поправить, назвать их changeFilter и clearFilter.

Спасибо за замечание, поправлю в статье вычисление filter

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

Публикации