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

Што коштае вас выкарыстоўванне useEffect для запытоў і якія змены прыносіць TanStack Query

Чаму захаванне дадзэных на адказае прычыны падвойных запросоў, шаблонавага коду і застарэлых клаусураў у React, і як ключы, параметры staleTime і механізм анвалідацыі у TanStack Query спакойваюць гэтыя проблемы.

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

Крыніцы: Stop Using useEffect for API Calls, Use React Query Instead

Зялэнне дадзейнаў унутрь useEffect — гэта першы патэран, які выучаюць большасць разрабоў React, і ён добра працуе для адной запыткі на аднай сторанцы. Калі праект расте, той жа патэран таямніча збільшвае колькасць запытак, павторюе стандартны код у кожным компоненте і спрыяе выкліканню тонкіх багоў, зв’язаных з залежнасцямі. У гэтым артыкуле пояснюецца, звідзе выходзяць гэтыя наследкі, і як TanStack Query (ранейша React Query) рашае кожны з іх, ўжо калі вы зможыце адляцца ад пачатковага падходу і выбраць, што налаштаваць.

Патэран, з якога пачынаюць усі

Звычны варіант — гэта эфект з порожнім масавым параметрам залежнасцяў, які вызывае функцыю fetch, калі компонент запускаецца:

useEffect(() => {
  fetchData();
}, []);

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

Чаму запыт выконваецца два разы і справжня прычына гэтага

Частым першым здзівленнем є тое, што пад час завантажэння сторонніцы ў вікні сеті можна побачыць той самы запыт два разы. У режыме разработкі StrictMode у React намеравана запускае, знімае і практычна знову запускае компоненты, ўбачыць эфекты, якія самі сабою не чыстяцца. Дублікат запыту є намеровым у режыме разработкі і не выкарыстоўваецца у фінальнай версіі продукту.

Двойныя запыткі ўсё-такі яўляюцца сімптомам чагось значныя ў працэсе стварэння інтерфейсу: запыткі на адзержванне дадзэнняў, базаваныя на эфектах, прычаленыя да ціклу жыцця компонента. Кожная перзапускання компонента выклікае новы запыт, нават калі дадзеныя вже ў наявнасці. Компоненты, якія перзапускаюцца раз за разам, такія як панелі вкладакоў, модальныя вікна і рядкі спіса, продовжваюць здзейсніваць запыткі да сервера, чаго яшчэ больш адбаротвае сервер і робіць інтерфейс медленнэй, чым трэба.

TanStack Query аддзеляе дадзеныя ад самога компонента. Рэзультаты зберагаюцца ў спяльнай кэш-сістэме пад ключам запыткі. Калі іншы компонент, або той самы пасля перзапускання, запрашвае ключ, дадзеныя па якому ўсё щэ считаюцца свежымі, бібліятэка вяртае значэння з кэша, не выканаліваючы запыт у сеті. Калькольве компоненты, якія запрашваюць той самы ключ адразу, таксама дзеліўцяюць одним запытам у працэсе обработкі.

Ручны кераванне станам загрузкі і адзяўок

За дапамою useEffect кожны запит выклікае неабяжна тройку зменных стану: для дадзейнаў, пазнакі завантажэння і памылкі:

const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

Як толькі такое трэба практыкуваць для кожнага вызову API, наступае вельмі вялікі кантэнт майже ідэентычных кодаў, пры чым кожны з іх мае шанс на памылку. Класычная з іх – гэта забывчына аб перазначэнні значэння loading у сценарыі памылкі, што прыводзіць да таго, што індыкатор завантажэння не перастае працаваць. Іншыя проблемы – гэта непрыгледжанне адпаведзення, якое прыходзіце пасля таго, як компонент быў зняты, а таксама можлівасць таго, што спакойнае адпаведзення, якое прыйшло раней, заменяе новэйшае, калі вхідныя даны змінююцца быстра.

useQuery адразу вяртае data, error, isLoading і isError (а таксама болей дакладныя поля статусу), і ён сам адрабатвае ситуацыі, калі адпаведзі з’являюцца не па порядку. Неабыходны код з’яўляюцца ў компонентах, а спецыяльныя кейсы рашаюцца ў добра працаванай бібліятэке аднойчы, а не разоў у кодзе прыемліцеля.

Масэвы залежнасцей проты ключоў запыту

Эфекты залежаць ад правильнага выбору масэвы залежнасцей. Якщо працягнуць неякі значэнне, якое чытае эфект, то будзе стварацца застарэлы клоузур, які выкарыстоўвае застарэлыя параметры для запыту, і гэта можа стаць прычыной багу, якій патрэбны гадзіны на выявленне. Якщо у масэвы залежнасцей будзе об’ект або функцыя, якія перзначаюцца ў кожны раз, калі выкананы рендер, то эфект будзе выкананы ў кожны раз, і запыт будзе адправляцца з кожнага разу.

TanStack Query заменяе гаданне на болей шчырокаю даговорнасць: вы задаеце кожнаму запиту ключ і функцыю загрузкі. Ключ павинен уключаць усія зменныя, ад кых залежыць запит, напрыклад назву ресурса плюс ID пользователя або фільтр. Калі ключ змянюецца, запускаецца новы запит; калі ён застаецца тым жа, викорыстоўваюцься даныя з кэша. Ключы порушаныя за ўместам іх зместу, а не па ссылцы, таму стварэнне новага масіву на кожным адрасаванні не спрычынае дадатковых запускаў. Перзапуск запита кантролюецца за дапамогою явных правіл, такіх як монтаванне компонента, вярнэнне фокуса на вікнаце або павторны з’яўленне сеті, у поўнасці разам з параметрам staleTime, які вы настаўляеце.

Вам усё рава трэба думаць пра залежнасцях, але вы выражаеце іх раз у ключы, замест таго каб падтрымваць масіў, які павінен застацца сінхронным з тэлам эфекту.

Кэш, staleTime і анаверценне

Трыя функцыі адпраўдваюць большую частку разніцы ў кождадзённай работе.

Кэш

Пасля таго, як даныя па ключу былі запрашаны, яны застаюцца ў кэшы. Калі корыстнік пакідае сторонню і зноў вяртаецца, результат з кэша адразу ж прадстаўляецца, а якщо даныя застарелі, бібліятэка запрашае іх зноў у фоне і апдэйтуе экран, калі прыходзіць новая адпаведь.

staleTime

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

invalidateQueries

Пасля мутацыі, такой як апдэйт прафіля чыста стварэнне пасткі, вы вызываеце queryClient.invalidateQueries з адпаведным ключам. Адпаведныя запыткі пазначаюцца як застарэлыя, а тые, якія нараз працуюць, автаматычна падзейліваюцца занова. Не трэба ручна падзейліваць данныя пасля кожной мутацыі і не трэба вручную копіюваць адпаведзіце сервера ў локальны стан.

Разам гэтыя тры элементы пакрываюць большую частку проблем, якія ствараецца падчас запыткаў на адпаведнасць. Чырвоны глэбокія інформацыі пра арганізаванне ключоў, опцый запыткаў і мутацый у вялікых масштабах, адзірніце нашыя інструкцыі па структураванню шару дадзэйна TanStack Query.

Тыповая сцэнарій міграцыі

Разглянем панель керування Next.js SaaS з калькуламі, пры якіх кожны з’являецца з сабею даныя за дапамою useEffect. Адказна зміна калькулама перзначае панелі, таму кожная такая зміна выклікае новы набор запитоў. Бэкенд перасілае значна больш трафіку, чым трэба, і корыстувачы бачаюць сповольненне падчас завантажэння.

Міграцыя зазвычай складаецца з трохох крокаў:

  1. Заменіце кожны эфект на useQuery і ключ, які фіксуе всі параметры запита.
  2. Задайце кожнаму запыту значэнне staleTime, якое відображае частоту рэальных змян яго даных.
  3. Пасля кожной мутацыі анулюйце ключы, якія паўтараюць даныя, якія ёй паўтараюцься.

Рэзультатам будзе павольнейшая панель керування, меншыя запыты да бэкенду і кращы досвід для корыстувачаў, а таксама менш код у кожным компоненте.

Дзе ўсё ж такі паслужыць useEffect

Это не прытак для чыстага адзёрвання useEffect. Ён застаецца правым інструментам для сінхронізаціі з элементамі за межамі React, якія не ўжоўна ўособліваюцься як даныя типу запит-адказ: падпісанні, абэрватары змян DOM, таймеры і відэжеты трохіх сторон. Аднак для вызоваў API ў прыстрыме, які расте, болей падходзіць спецыяльная бібліятэка для керавання станам сервера, адтыму што яна зменшвае навантажэння на бэкенд, адмахвае зайвы код і з самага пачатку забезпечвае кэшаванне і анулювання данных.

Есць таксама случаў, калі бібліятэка єсць занадта великай для вашых патрэб, напрыклад, калі мова йдзе пра невялікі дапыт з аднам чы роўным двум запитамі. Для такіх ситуацыяў можа паспеліць невялікі спецыяльны хук з належным абрываннем; наша стаття паўінна прыняць вялікую частку ў розумеўцы гэтага компромісу. Якщо вы не впевнены, спачатку пераместіце адну стораніцу і парабяруйце колькасць запытанняў у вікні сеті.

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

  • Двойны запыт у режыме разработкі выходзіць з StrictMode; проблема ў режыме продакшна заключаецца у тым, што запытанні, базаваныя на эфектах, павторююцца ў кожны раз, калі элемент монтуюцца.
  • Ключ запытання заменяе массив залежнасцей: неабходна уключыць усі данні, на якіх залежыць запытанне, і тады будзе адбывацца кэшаванне і павторны запыт.
  • Значэння staleTime па замовчанню є равным нулю, таму якщо хочаце ухіліцца ад павторных запытанняў у фоне пасля перамонтавання, яго трэба спецыяльна задаць.
  • Пасля змян неабыць супакоўаныя ключы, замест таго каб ручна падтрымліваць даны або сінхронізаваць стан.
  • Выкорыстоўваць useEffect для сінхронізацыі з зовнішнімі системамі, а не для завантажэння дадзеных сервера.