Галоўная / Артыкулы

Чы гэты хук заслуговае на свое месца? Дзесяць запытанняў для адгукнуцца пра код на React

Выучыце дзесять конкрэтных запитанняў для адзінакоўкі, якія выявляюць зайвыя станы, застарэлыя эфекты, цыклы render, непатрэбную мемаізацыю і занадта большыя компоненты ў коде React Hook.

Выдавец: REACTAPP.TOP6 хв чытання1324 слоў

Крыніцы: Hooks

Большая частка багоў у React Hook не выглядае як багі ў патронцы прыяўлення змян. Недастатак залежнасцей, эфект, які копіюе параметры ў стан, або useCallback навакола обробніка, які ніхто не мемуе, праходзяць перагляд пад час рэвю, а пазней выражаюцца ў застарэлых экранах, дадатковым атрыбутаванні элементаў чы скупленых слухачах. Ёсць дзесять частых памылак у Hook, якія ператвореныя на запитанні для перагляду, якія можна задаць будзь-яму компоненту, з апісаннем механізма кожной з іх і простейшаг варыянта коду, які зазвычай трэба.

Запитанні пра стан

Чы можна вылічыць гэтую значэння пад час атрыбутавання элементаў?

Звычным падчынам ёсць зберагачэнне fullName у стане і викорыстоўванне эфекта для вызову setFullName(`${firstName} ${lastName}`) кожны раз, калі змянюецца аднойчы з імен. Компонент атрыбуецца разаў з старым значэнням, эфект запускаецца пасля атрыбуэння, а функцыя-зменнік запланавае другое атрыбуэнне. На адну кадру інтерфейс становіцца неоднаковым, і трэба падтрымвать дапаможную частку, якая рухаецца.

Замест таго вычысляйце яго так: const fullName = `${firstName} ${lastName}`;. React перзапускае компонент праз кожнае атрыбуэнне, таму значэння завжды сінхронаванае з його вхіднымі даннымі. Якщо ўсё можна вывести з пропсаў або стану, то гэта не ёсць стан. Адносна таксама пагляніце чаму сінхронізацыя стану з useEffect прыносіць наследкі.

Чы не зберагае компонент больш стану, чым яму трэба?

Той самыя адчыненнія, але у большам масштабе: форма з трыма вызовамі useState для firstName, lastName і fullName. Кожная змена імені тепер павінна быць адбітая і ў fullName, а якщо гэта зрабіць не стануць, выйдзе баг. Залейвайце два незалежныя поля для вводу, а трэцяе вылучайце з іх. Для кожнага useState спытайцеся, чы можна перзначыць яго значэнне на адной з іншых значэнняў; як так, то ўдаліце яго.

Чы павінна змена гэтага значэння апдэйтаваць экран?

Стан існуе для керавання процэсам выклікаў. ID таймера, вузел DOM, пыльнейшая значэнне пропа або інстанцыя трэцьей стороны (карта, графік, кліент сокета) зазвычай не вплываюць на тое, што будзе адрасаванае, таму useState(null) для такіх элементаў означае, што кожная прызначэнне спрычынае непатрэбны выклік. Усё-такі лепей викорыстаць const timerRef = useRef(null);: запіс timerRef.current застаецца незменным пасля кожнага выкліку без неабяжнасці яго практычнага выкліка. Аднойчы ж, нельга чытаць значэнне ref, каб адначасова вярнуцца да таго, што трэба адразаваць. Якщо інтэрфейс павінен відбіваць даныя, то гэта жоўткавае значэнне — стан.

Запытанні пра эфекты

Чы ў масэве залежнасцяў пераказана ўсё, што чытае эфект?

Якщо пасаваць [], каб эфект «выканаўся аднае раз», як у useEffect(() => { fetchUser(userId); }, []);, то userId застаецца незменным з першага атрыбутавання. Калі пропы змянююцца, нічога не перзапрашваецца, і інтэрфейс паказвае некоректнага пользователя. Ёжы [userId] выправляе гэта.

Масава не ўжо інструмент для кантролю часу выканання; яна прызначае, якія значэнні чытае эфект. Якщо эфект выканаўся занадта часта, трэба змяніць тое, што ён чытае (пераместіць функцыю ўсередину, спрыяцца прымітывам у змену на об’ект), а не хаваць залежнасці. Правило лінт-перагляду react-hooks/exhaustive-deps ловіць практычна всі такія случаи, таму трэба спрыймать яго паведамленні як адыяхи. Эфекты, якія перзапрашваюць данні, таксама должны ігнараваць або завершваць застарэлыя запиты пад час чысткі, калі userId швытка змянюецца.

Чы можа гэты эфект сам сабе спрыяць?

useEffect(() => { setCount(count + 1); }, [count]); — гэта цікл: апдэйт зменшае значэнне count, што прыводзіць да паўтарнага запуску гэтага эфекту без канца. Функцыя setCount(prev => prev + 1) чытае апошню значэнне без неабходнасці выкарыстоўвання count у клозуре, таму залежнасць не патрабуеяцца, і эфект запускаецца толькі пад час стварэння компонента. Заўважыце, што ён усё равно збільшае значэнне аднае раз пры кожным стварэнні компонента, а ў режыме Strict Mode стварэння відбываецца два разы пад час розработы, таму магчыма пабачыць, як значэнне збільшаецца на два елементы.

Лепшы вопыт часта ў тым, чы рэальна апдэйт патрабуецца ў эфекте. Адпаведзі на дзеяння корыстувальца падаюць у хэндлеры змян, а вырахаваныя значэння — пад час відрасці компонента. Эфекты служаю для сінхронізаціі з системамі, якія не ўскладненыя React.

Чы эфект скасоўвае тое, што ён насталяв?

Эфект, які выключаецься толькі праз вызванне window.addEventListener('resize', handleResize), дадае новага абэрнер на кожным запуске і ніколі яго не адчыняе. Паверніце функцыю для чысткі, якая вызывае window.removeEventListener з тым самым хэндлерам. React выконвае яе перад паўторным запускам эфекту і праз адчыненне. Без яе вытвараюцца відклэканні памяці, хэндлеры запускаюцца колькі разоў, а таксама выконваюцца апдэйты для компанентаў, якія вучынены.

Короткая перапактовка: кожны add, connect або setInterval у эфекте патрабуе адпаведнага remove, disconnect або clearInterval у яго значэнні, якое павертаецца. аудыт useEffect з дзесяцьма распашчэннямі рассказвае пра іншыя способы неудачы эфектаў.

Запытанні пра мемоізацыю

Чы рэальна гэты вычысленні ўскладнены?

Захаванне спалучэння стрынга ў useMemo з [user] як залежнасцю коштае больш, чым зарабляе. React як і раней выдзеляе калебэк і масів, а таксама порыхуе залежнасці ў кожны раз, калі відбываеся рендарынг, што ўсё ж є болей складным задачай, чым проста з’едыніць два стрынги. Код становіцца важкім для чытання, а якщо user ў кожны раз є новым об’ектам, мемо-функцыя адноўлівае своі вычысленні ў будзь-якім случае.

Выкарыстоўваюце простую выразу. Зверніцеся да useMemo толькі для дасліджуема складных задач, такіх як сортаванне чы адсортаванне вялікага списку, або калі мемаваныя дзецкія элементы чы іншыя хукі патрабуюць стабільнага аб’ектавага рэферэнсу. Адначасова пераканайцеся за дапамою профілера React DevTools.

Чы ўсё-такі якаясь частка залежыць ад ідэнтычнасці гэтай функцыі?

useCallback зберагае стабільны кансэл функцыі, што мае значэнне толькі тады, калі ў чым-небудзь паўтараюцца кансэлы: у дзецкай элементаў, якая абгорнутая ў React.memo чыстае лісты залежнасцяў іншага Hook-а. Абгорткі кансэлу обработчыка кліку, який проста логавае, прыўязанага да звычайнага button, не прыносіць нічога, таму што DOM не паказвае увагі на тое, чыя ўжо функцыя. Напісайце звычную функцыю і адначасова выкарыстоўваеце useCallback, калі можаце назваць элемент, які яе патрэбуе. Кампайлер React можа аўтаматычна застосаваць такую мемаізацыю; пераканайцеся ў актуальных дасведчэннях па статусе гэтага функцыяналу у вашай настройцы.

Пытанні пра структуру

Чы кожны Hook выклікаецца без аднаковых умоў, у той самы порядку?

Заявленне useState толькі ўсередине if (isLoggedIn) наражае на парадоксы Правіл хуков. React пасвячае хуки за ўзроўнем вызову ў спісе для кожнага компонента, а не за іменем. Якщо пропусціць один вызов, кожны наступны хук будзе чытаць данні з суседней пазухі, што прызведае да некоректных значэнняў або памылкі пад час выконання ў зв’язку з змяненым колькістю хуков.

Вызывайце всі хуки на верхнім роўні, а потым далей развіваюце логіку: спачатку заявіце стан user, а пасля, якщо корыстнік не заўвязаны, верніце null. Тое ж самае стосуецца ціклам і ранніх вызначэнняя. Па деталям адгледзіце прычыны існавання Правіл хуков.

Чы не виконвае гэты компонент занадта многа задач?

Компанент, які сумяшуе сотні рэядкаў логіки запрашання дадзейнаў, стану і бізнес-логіки з маркапам, ўскладняе чытанне, тэставанне і змяну. Выдзельце цюю логіку ў спецыяльны Hook. Hook useUserData(userId) керуе станам user і ефектам, які вызывае fetchUser(userId).then(setUser), калі змянюецца userId, а пасля вяртае user. UserProfile спраўляецца ў const user = useUserData(userId); плюс атрыбут user?.name.

Гэта дае вам:

  • Магчымасць паўтарнага выкарыстання без копіювання логіки межы компанентамі.
  • Тэсты логіки, незалежныя ад маркапам.
  • Корачэйшыя компаненты, якія лёгка ўскладніць.
  • Одна пазуха для змян у запрашанні дадзейнаў аб кэшаванні.

Памятайце, што кожны вызывальнік спецыяльнага Hook атрымлівае свой сабэй стану: ён дзеліць логіку, а не даны.

Галоўныя выводы

  • Вырашаныя значэнні — это выразы, а не стан та ні эфекты.
  • Зменшайце стан да мінімуму; значэнні, якія ніколі не прабываюць на экране, кладзіце ў refs.
  • Масівы залежнасцей описваюць, што чытае эфект, таму пераканальвайце іх адкрыта.
  • Кожны эфект, який ўстановляе якісь элемент, павінен яго зняць.
  • Мемаізавайце толькі тады, калі це прабачаеяцца мераваннем або корыстувальнікам, чутлівым да рэферэнаў.
  • Выклікайце Hooks без аднаковых умоў, а большыя блакі логіки перакладзіце ў спецыяльныя Hooks.

Большасць гэтых проблем выходзіць з таго, што люди за прыzwычкай выкарыстоўваюць Hook. Задаць пытанне "Чаму нам насправдэ ж трэба гэты Hook тут?" пад час адзінаковага аналізу — это проста, і гэта захоўвае час на дагледжэнне бяга програмы.