Детальныя інструкцыі TanStack Query: запиты, кеш, мутацыі та оптымістычны інтерфейс.
TanStack Query керуе станам сервера як менеджер ресторана: спільныя ключы кешу, настройкі свежасці, скоординаваныя мутацыі, анулювання та оптымістычныя апдейты, якія працуюць пад вашым HTTP-кліентам.
Сэгодняшнія тэмы — TanStack Query (ранейша React Query): бібліятэка, якая ператварае складны процес запрашэння даных з сервера на прыглядныя процесы кэшавання, падтрымкі свежасці даных і ўтварэння змян. Аналагія з ресторанам высокай класы дапамагае легчэй запам’ятаваць усі элементы — зала для абедоў як UI, кухня як бэкенд, параднікі як HTTP-кліенты, а менеджер — TanStack Query.
Што такое TanStack Query?
У звычным дапрыемку UI запрашае даныя у бэкенду за дапамогою fetch або Axios. Гэтыя кліенты не дужа эфективны ў коордынацыі. Якщо пяць компонентаў адразу запрашаюць той самы меню, можа з’явіцца пяць окрэслівых выездоў на кухню. Якщо хтось запрашае суп дня, а іншы гасць — за дзесять секунд пазней, наўмны параднік зноў ідзе на кухню. Гэта перавантажвае сервер і спам’чвае роботу залу.
TanStack Query выступае як главны службавык і менеджер ресторана: ён координуе процесы запрашэння, кэшавання, сінхронізацыі і апдэйта стану сервера, ўнаследке кухня не падвергаецца надмернам запыткам.
Axios / Fetch проты TanStack Query
Пачатківцы часта думаюць, што TanStack Query заменяе Axios або fetch. Гэта не так.
- Fetch і Axios — гэта службавыкі. Яны перадаюць запыткі на кухню і прыводзяць адпаведныя адказы. Яны не памятаюць пакульшых запыткаў, не ацэнююць свежасць інфармацыі і не координуюць дзеяння іншых службавыкаў.
- TanStack Query — гэта менеджер. Ён наймае службавыкаў для выпанення заведамацтваў. Ён памятае, што было прынята, чы рэшты ўсё яшчэ сцялівае, якія столы выкарыстоўваюць аднойчыныя зошыты, і калі трэба пасłaць каго-небудзь назад пасля таго, як кухня змяніла страву.
Вы яшчэ пісваеце тэлы queryFn, якія вызываюць Axios або fetch. TanStack Query абгружае гэтыя вызывы за дапамою ключаў кешу, адмовы від дублікацый, павторных спроб і хуков жыцёвага циклу.
Што такое TanStack Query? (карта можлівасцей)
У кождадзённай работе з React найважлівейшыя шасць можлівасцей:
1. Запыты (useQuery): аблікаванне дадзеных
Декларатыўныя запыты, ключаваныя queryKey, якія выконваюцца за дапамою queryFn.
2. Кешаванне: «мозг» каліента
Рэзультаты зберагаюцца ў памяці, ключаваныя ключам запыту, тады калькі компонентаў можаюць дзеліцца адной сеансай з’язку.
3. Свежасць: staleTime проты gcTime
staleTime вяршыць, калі дадзеныя з кешу лячуцца достатна застарэлымі, каб іх павторна абгрузіць. gcTime (сбор сметлі) вяршыць, як дуго застарэлыя ентры кешу застаюцца пры ўсёй памяці, перш чым іх адмаўляюць.
4. Мутацыі (useMutation): змена дадзейнаў
Запісы — стварэнне, апдэйт, выдаленне — выкананы за патрэбай, а не пад час запуску.
5. Анулюванне запыткаў: знішчэнне меню
Пасля успешной мутацыі пазначаюцца супакойныя запыткі, ў результате чаго інтерфейс снова синхронізуецца з серверам.
6. Оптымістычныя апдэйты: досвядзек у рэстаране з зоркай Мішлен
Негараздзе кэш апдэйтуецца немедленна, у разы ўзбіччя — практыкуецца вярнэнне да пачатковага стану, а пасля — выканана остаточная синхронізацыя.
У рэште гіду кожная з эйхаўных можнасцей расглядзіцца на прыкладах з рэстаранамі і конкретнымі фрагментамі коду.
1. Запыткі (useQuery): атрыманне меню
Запыток паказвае, што вы хочаце і як гэта атрымаць:
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
// The waiter function (Axios)
const fetchMenu = async () => {
const response = await axios.get('/api/menu');
return response.data;
};
function MenuComponent() {
// The Manager (TanStack Query) orchestrating the process
const { data: menu, isLoading, isError, error } = useQuery({
queryKey: ['menu'], // The label for this specific data
queryFn: fetchMenu, // The waiter doing the fetching
});
if (isLoading) return <div>Waiter is walking to the kitchen... Loading Menu...</div>;
if (isError) return <div>The kitchen is on fire! Error: {error.message}</div>;
return (
<ul>
{menu.map((item) => (
<li key={item.id}>{item.name} - ${item.price}</li>
))}
</ul>
);
}
queryKey — это названне файла ў зошыце адпаведнага менаджэра: ['menu'], ['soup'], ['allergies', tableId]. Аднаковыя ключы выкарыстоўваюць аднойчыны кэш і усунуць дуплікацыі ў запитах, якія адправляюцца. queryFn — это функцыя, якая вяртае обявленне з дадзеннямі.
Калі выкананы першы запит, флагі isPending / loading дазволяюць інтэрфейсу паказваць тымчасовыя элементы. Адныя проблемы выказваюцца через isError і error, а успешныя дадзенні з’яўляюцца ў атрыбуте data і застаюцца доступнымі для всіх компанентаў, якія стежыць за гэтым ключам.
Што такое умова супернікавання?
Аналагія з ресторанам
Уявіце двух гасцоў, якія просіць суп, а кухня работае медленна. Запіт стола А, які прыйшоў пазней, не должен перазапісваць новышы запіт стола B. Без скоординавання той запіт, які завершыцца апошнім, будзе прыйняты — нават якщо ўжо застарэлы.
Як гэта выглядае ў React (useEffect)
Ручныя запыты за дапамою useEffect часта забываюць прыемліваць логіку абаранавання. Калі быстро пераходзіце межа сторанак, старэйшы адпаведны рэсурс можа змяніць стан пасля таго, як быў запусцены новейшы запыт.
Як TanStack Query рашае проблему супернаганяння
Бібліятэка ведае пра запыты, якія ўжо выпалююцца, па ключу, може абаранаваць іх за дапамою AbortSignal, калі гэта падтрымвана, і гарантуе, што элементы UI бачыць адпаведныя змены ў кэшы, а не хаотычныя змены стану праз setState.
2. Кэшаванне: зошыт керуючага
// Waiter function
const fetchMenu = async () => {
console.log("Waiter is walking to the kitchen!"); // We can track how many times this runs
const response = await axios.get('/api/menu');
return response.data;
};
// Component 1: The Sidebar
function MenuSidebar() {
const { data } = useQuery({ queryKey: ['menu'], queryFn: fetchMenu });
return <div>We have {data?.length} items today!</div>;
}
// Component 2: The Main Display
function MenuMainDisplay() {
const { data } = useQuery({ queryKey: ['menu'], queryFn: fetchMenu });
return <div>{data?.map(item => <p>{item.name}</p>)}</div>;
}
Калі першы компонент запускаецца з параметрам ['menu'], керуючы вышле сігнал для запыту. Калі за кальканус мілісекунд другі компонент запускаецца з тым самым ключам, ён чытае даны з кэшу, а не запускае новы запыт. Самэй гэтая дедуплікацыя дапамагае панелі керування з многама карткамі, якія выкарыстоўваюць аднаковыя запыты, працаваць швайна без неабяжных глобальных сховішчаў.
Менеджар у дзеянні (па кроках)
- Компанента A запускаецца → не застаўлены кэш → сеть.
- Прыходзіць адпаведнасць → запіс у кэш → компанента A атрымлее візуальны выгляд.
- Компанента B запускаецца з тым самым ключам → кэш застаўлены → компанента B атрымлее візуальны выгляд негараздо быстрэй.
- Согласна правілам свежасці, пазней можа адбыцца фонавая перзапіска дадзеных без блакання першага атрымлення візуальнага выгляду компаненты B.
Стан сервера павінен знаходзіцца ў TanStack Query; справжній стан UI кліента (відкрыты модальны элемент, выбраная вкладка) можа застацца ў стане React або ў лёгкай базе дадзеных кліента.
3. Свежасць: налаштаванне staleTime і gcTime
1. staleTime: чы рэальна гэта інфармацыя ўсё яшчэ актуальная?
const { data } = useQuery({
queryKey: ['soup'],
queryFn: fetchSoup,
staleTime: 1000 * 60 * 30, // 30 minutes
});
За дапамогай staleTime: 10_000 даны, які ўтворыліся менш чым за дзесять секунд, вважаюцца свежымі: пры павторнай установке іх викорыстоўваюць без падтрымкі занова. Калі воны стануць застарэлымі, спостерагачы можаць запускать фонавую падтрымку занова (практычна пры павторнай установке, фокусе вікна або перыяднанні — залежна ад стандартных настаўк і параметраў). Значэнне staleTime выбіраецца з учоўлень пра мінутнае зменыстае падання данных: спіс страв дня можа быць короткім, а спісы краў — дужа дзьвяным.
2. gcTime: чы магу я пакінуць гэты документ?
const { data } = useQuery({
queryKey: ['allergies', 'table4'],
queryFn: fetchAllergies,
gcTime: 1000 * 60 * 60 * 24, // Keep in memory for 24 hours
});
gcTime керуе тым, як давго элемент кэша застаёцца пасля таго, як усі спостерагачы былі адключены. Короткае значэнне gcTime шырэй вільняе памяць; дзьвяныя значэнні робяць повторныя выходы на сторану мгновеннымі. Не плутаць яго з staleTime: застарэлыя даны можаць заставацца ў памяці пакуль не будзе адбытаў процес збірання сметлі.
Апошні секрэт: stale-while-revalidate
TanStack Query з радасцю паказвае застарэлыя данні пад час павтаральнага запрашэння ў фоне. Корыстувальнікі момантальна бачаюць последняе вядома меню; калі кухня падтвердзяе апдэйты, зошыт павтарае запрашэння. Самэй гэтым патэрнам бібліятэка здаецца быстрейшай за спінеры ў кожны раз, калі яе відвідваюць.
4. Мутацыі (useMutation): дадзенне новага стравы
Што такое мутацыя?
Мутацыя змінюе стан сервера — адправка замовлення, рэдагаванне прафіля, выдаленне каментара.
Аналагія з ресторанам: адправка замовлення
Офіцыянты не адправляюць замовлення автаматычна, калі гаспадар сядзе; яны чакаюць чыткага запиту. Мутацыі працуюць адносна так: яны выкананыя, калі вы вызываеце mutate або mutateAsync.
Код: стварэнне формы замовлення
import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
import { useState } from 'react';
// 1. The Waiter Function (The actual network request)
const placeOrder = async (orderData) => {
// We are using POST because we are creating a new order
const response = await axios.post('/api/orders', orderData);
return response.data;
};
function OrderForm() {
const [dish, setDish] = useState('');
// 2. The Manager orchestrating the mutation
const mutation = useMutation({
mutationFn: placeOrder,
// We can also trigger side effects right here!
onSuccess: (data) => {
console.log("Chef says: Order confirmed!", data);
},
onError: (error) => {
console.log("Chef says: We have a problem.", error.message);
}
});
const handleSubmit = (e) => {
e.preventDefault();
// 3. Triggering the mutation and passing the variables
mutation.mutate({ dishName: dish, tableNumber: 4 });
};
return (
<form onSubmit={handleSubmit}>
<input
value={dish}
onChange={(e) => setDish(e.target.value)}
placeholder="What would you like?"
/>
{/* Notice how we use isPending to disable the button so they don't double-order! */}
<button type="submit" disabled={mutation.isPending}>
{mutation.isPending ? 'Sending to Kitchen...' : 'Place Order'}
</button>
{/* Handling the feedback */}
{mutation.isError && <p style={{ color: 'red' }}>Failed: {mutation.error.message}</p>}
{mutation.isSuccess && <p style={{ color: 'green' }}>Order placed successfully!</p>}
</form>
);
}
Працюйце з параметрам onSuccess для адказу працэй, навігацыі чырага або невалідазавання. Ожывайце mutateAsync, калі трэба чакаць завершэння у обробчыках адправкі.
Дапаможныя деталі для разработчыкаў
1. Ён не запускаецца аўтаматычна
У працоўніцу з запытамі на відміннасць, мютацыі застаюцца нерухомымі пакуль іх не вызначаюць — гэта запобегае случайным збіркам пад час адрасавання.
2. isPending проты isLoading
У версіі v5 лепш выкарыстоўваць isPending для пазначэння статусу мютацыі. Споўна супрацавайце з блокаваннем UI па гэтым флагу.
3. Запобежанне проблеме двойнага клікання
Блакайце кнопку адправкі, калі isPending ў стане true, каб гасці не моглі адправляць дублікаты замовленняў.
5. Невалідазаванне запыту: паведамленне менаджэру пра апдэйт зошыка
Проблема: застарэлы зошык
Пасля таго, як шафард дадзе новая страва, столы, які ўсё ўжо адображаюць кэшаваныя меню, будуць бачыць сяродзінную спіс до таго часу, пакуль не будзе адбыта перзапіс.
Рашэння: анулюванне запытак
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
function AddDishForm() {
// 1. Get access to the Manager's office
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (newDish) => {
const response = await axios.post('/api/menu', newDish);
return response.data;
},
// 2. The magic happens HERE in the onSuccess callback
onSuccess: () => {
// 3. Tell the Manager to rip up the menu notepad
queryClient.invalidateQueries({ queryKey: ['menu'] });
console.log("Menu invalidated! The Manager is getting a fresh copy.");
},
});
// ... form code
}
invalidateQueries пазначае адпаведныя запыткі як застарэлыя і спрычынае ўзнавленне даных для актыўных спостерагачаў.
Што самэ працуе, калі вы вызываеце invalidateQueries?
Адпаведныя запыткі стаюць застарэлымі; спостерагачы, якія ўстановлены, перзапішуць даны; элементы, якія не ўстановлены, чакаюць да наступнага установлення (залежна ад gcTime). Кухня застаецца асновай правды; нотатнік павінен перзапісаць даны.
Развітыя ідэі: размытае падабенне
Часткова анулювання:
queryClient.invalidateQueries({ queryKey: ['menu', 'lunch'] });
Альбо анулюваць цэлы прыфікс:
// This rips up the breakfast, lunch, and dinner notepads all at once!
queryClient.invalidateQueries({ queryKey: ['menu'] });
Фазы прыцелкі праблематычных прыдзеў разрываея блокнеты з нотаткамі пра завтрак, обед і вечарую, калі вы шырока анулюеце ['menu'] — гэта моцнае і апасныя. Лепш выбраць найвужэйшы ключ, які будзе падтрымваць правильнасць інтерфейса.
Золатая правіла мутацыяў
Кожны успешны запіс должен або анулюваць чытання, якія ад него залежаць, або рэгулярна апдэйтаваць кеш. Не чыніць змян у чытаннях — гэта спосаб, як інтерфейсы «бягаюць» пасля зберагання дадзеных.
6. Оптымістычныя апдэйты: досвяд Мішлен
Аналагія: доверы менеджера
Довераны менеджар можа запісаць новая піва ў рачунак прытаманне кухні — а потым стерць яе, якщо піва няма.
Тры сталыні оптымістычных апдэйтаў
Унутры useMutation:
onMutate: зупініць суперсечныя запыты, зробіць копію кеша, негайна запісаць оптымістычныя дадзеныя.
onError: адзначыць снэпшот зноў, якщо сервер адмовіць.onSettled: анулювацыя (альбо іншая сінхронізацыя), каб кэш паспадзяў да базы данных, незалежна ад таго, чы робіта успелася чы ні.Код: адносна оптымістычная дадзенне блюда
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
function AddDishForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (newDish) => {
const response = await axios.post('/api/menu', newDish);
return response.data;
},
// 1. The millisecond the user clicks submit...
onMutate: async (newDish) => {
// A. Cancel any outgoing refetches so they don't overwrite our optimistic update
await queryClient.cancelQueries({ queryKey: ['menu'] });
// B. Take a snapshot of the current menu (The Eraser Backup)
const previousMenu = queryClient.getQueryData(['menu']);
// C. Optimistically update the Manager's notepad right now!
queryClient.setQueryData(['menu'], (oldMenu = []) => {
// We fake an ID for now, the real ID comes from the database later
return [...oldMenu, { ...newDish, id: Math.random().toString() }];
});
// D. Return the snapshot so onError can use it if things go wrong
return { previousMenu };
},
// 2. If the Kitchen catches on fire...
onError: (err, newDish, context) => {
// Use the eraser! Roll back to the snapshot we saved in onMutate
if (context?.previousMenu) {
queryClient.setQueryData(['menu'], context.previousMenu);
}
console.error("Chef says no! Rolling back.", err);
},
// 3. Always run this at the very end, success or fail...
onSettled: () => {
// Tell the Manager to get the real, final menu from the database
queryClient.invalidateQueries({ queryKey: ['menu'] });
},
});
// ... form code
}
Зважайце на ануляванне запускаемых запытак, структуру снэпшота і шляхі анулювання. Оптымістычны інтерфейс даўа вражанне мгновеннасці, але ніколі не павінен заставіць кэш у стане очаквання, калі кухня адмовіць.
Аб’еднанне ключоўых элементаў
- TanStack Query — это асінхронны менеджер стану, а не з’явальнік дадзенняў. Ён абгортае Axios/
fetch, каб сетевыя проблемы былі прыведжаны да прагнозавання. - Ключ запыткі — гэта все. Ён керуе усуненням дублікаў, кэшаваннем і абменам межа компонентамі.
invalidateQueries або рэтельна створаныя оптымістычныя апдэйты, каб кліентскі нотап паспаўляўся з базой дадзейнаў.Практычныя стандарты для рэальных прыкладоў
Пачніце з разумнага значэння staleTime для пераважна статычных ресурсоў (мінуты), а таксама з короткага або нульовага staleTime для дадзейнаў, якія залежаць ад спецыфічных настроек корыстніка. Храніце queryFn у чыстам варыянте, які можна абрануць. Централізаваць ключы трэба ў функціях-фабрыках (menuKeys.list(), menuKeys.detail(id)), каб процес анвалідэйвання заставаўся типавым і адносным. У развіцкай среде логаваць падзеі кэшу, калі дыагноzuеце падвойныя запыты. Вядома, кращэ анвалідаваць кэш, чым робіць складныя ручныя маніпуляцыі з кэшам, пакуль экран сапраўды не будзе выкарыстоўваць оптымістычныя тэхнікі.
Частыя прычыны неудач
- Іспользованне нестабільных ключоў (кожны раз ствараюцца новыя об’екты) разбивае кэш.
- Забывчыця анулювання пасля мутацый прыводзіць да апыловых дадзеных.
- Установка несканчатлівага значэння
staleTimeбез стратэгіі мутацый замарожвае інтерфейсы. - Размешчэнне всіх флагаў кліентскага інтерфейсу ў кэшы запытак плутае межы стану сервера.
- Чрэзмерна шырока ануляцыя (памылкі у стыле
queryKey: ['']) занова запрашоўвае весь контэнт.
Хто ўхіліваецца ад гэтага, таму рестаран працуе: параднікі ходзяць тады, калі трэба, блокнот менеджера застаецца чыстым, а гасці бачаць гарэную ўжоўку, не стаячы кожныя дзесяць секунд пры дзверях кухні.
Заканчэнне
TanStack Query здобывае свою популярнасць завялік тым, што керуе цыкламі жыцця сервера — чытанням, свежасцю дадзеных, запісам і сінхронізацыёю — а транспортныя аспекты залишаець на рашэнне Axios чыста fetch. Вы магчымае пазнакоміцца з наявнымі кантролемі (клавішамі), налашоўкамі свежасці (staleTime, gcTime) і процедурамі запісу (мутацыі, анавершэнне дадзеных, оптымістычныя апдэйты). За дапамою гэтага React-зявы перестаюць занова ствараць кэш запытоў у кожным useEffect і пачынаюць працаваць як адлікованая столовая.
Чаму метафора рэстарана продавяліваеся
Сетавыя «водапады» здаюцца абстрактнымі, пакуль вы не уявіце пяць слуг, якія бегуць за адной і той жа супам. Процес усунення дублікаў — гэта калі менеджер паднімае руку: адна прогулка, але паслужаныя багато столаў. Процес «служэння застарелага кантэнту пад час пераверкі» — гэта калі служба падае апошній распрытак меню, пакуль кареткіст пераглядае таблы з інформацыяй. Процес анулювання — гэта калі зрываюць сторунки, калі шафарзменяе рэцепты. Оптымістычныя апдэйты — гэта калі запісваюць замовленне гасця на рахунку ўсё раней, чым будзе падтверджэнне, і маюць стерачку пад рукою. Пры навчанні моладых спецыялістаў трэба паказаць гэтыя сцены раней, чым прыступаць да типавых элементаў TypeScript; так лепей захаваецца розумэнне.
Інтеграцыя з маршрутызаторамі та системамі аутэнтыкацыі
Калі даныя не ў глобальнам формате, ключы павінны включаць ідэнтыфікатор абонента чы ўжоўка: ['menu', restaurantId] або ['allergies', userId]. Пад час выходу з акаунта неабходна очысціць кэш, ўтаманнічваючы прасачванне сторунак з блокнота між рознымі корыстувальцамі. За дапамогою React Router чы падобных фрэймворкаў трэба запускать процес анавярэння толькі пасля дзеянь, якія вже знаюць, якія рэсурсы зменіліся, а не перзапрашваць усё пасля кожнага переходу.
Стратэгіі тэставання
Тэставаць маперы queryFn неабходна незалежна. У тэстах компанентоў іх трэба абгорнуць за дапамогою QueryClientProvider, выкарыстоўваючы новага кліента і задаўшы retry: false для забезпечэння детермінізма. Пераканацца, што мутацыі вызываюць invalidateQueries з неабходнымі ключамі. Для оптымістичных сцэнарыёў трэба сімуляваць адказы сервера і пераканацца, што система вяртаеся да поперадніго стану. Хаця б утаманнічваць выкарыстоўванне адного QueryClient у розных, некалякуючыхся тэстах без практычнага скасавання налаштоўкаў.
Заўважэнні пра выдатнасць
Большія спісы лепша размешчаць за дапамою пагінацыі або безканальных запыткаў, а не ў аднам вялікім ключы. Селектары (select) дазваляюць компанентам аблікаваць часткі дадзеных без паўтарнага атрыбутавання ў разы, калі мова не йде пра неканэксуальныя поля кешу. Рэзультаты queryFn трэба заставіць серыялізаванымі і стабільнымі. Аналізаваць ситуацыю з частым перзапыткам дадзеных, калі refetchOnWindowFocus работае з вельмі малым значэнням staleTime на ажыячых панелях керування — налагодзіць параметры для кожнага запытка окрема, а не глобальна.
Падход да міграцыі з прымітнага useEffect
Заменіце эфекты монтавання, якія задаюць тройкі дадзеных «завантажэння/памылка/данныя», на useQuery. Заменіце імператыўныя працэсары POST на useMutation. Удаліце самасталеваныя кешы. Заставіць экземпляры Axios для перехоплення пакетаў і заголовкаў аутэнтыкаты; перадаць іх у queryFn. Міграцыя ведзецца поэтапна: працаваючы з аднай экранам за раз, можна быстрэй усунуць багі, выкарыстоўваючы адночаснае выканання задач.
Заключны чэрніцкі список перад выпускам функцыяй
- Стабільны, іерархічны
queryKey. - Для домэна выбран явны
staleTime. - Мутацыя сопраўджваецца з анулюванням або оптымізам.
- У стане чакання UI блокуе подвойныя запускі.
- Падключаныя апавешчэння пра памылкі.
- Ключы, звязаныя з аутэнтыкацыяй, чыстаюцца пасля завершэння сесіі.
Якщо выпало ўсе шасць пунктав, TanStack Query перестае быць “яшчоўнай бібліятэкай” і стае тым тыхим калектаром, які быў неабходны вашай ўжынковай зале.
Праглед: завантажэнне меню між трыма компонентамі
Уявіце заголовак, які показвае суп на сьогодні, бокавыя панелі, які пераказваюць спецыяльнасці на обед, і галоўную панель, яка адображае весь меню. Без TanStack Query кожная панель могла бы стварыць свой useEffect і запытаць /api/menu. За дапамою спяльнага queryKey: ['menu', restaurantId] першы запуск виконвае запыт у сеть; іншыя проста чытаюць даны з кэшу. Калі шафар зменяе склад супа за дапамою адміністрацыйскага формы, выкарыстоўваючы useMutation, це неяксаверныя даны ['menu', restaurantId] апавежаюць пра це всі панелі, якія ўсё ўсё на экране. Гасці ніколі не бачаць трох празныясных супоў таму, што трох службавцоў не пагадаліся.
У гэтым прыкладзе ўжо є механізмы усунення дублікацый, спяльны кэш, мутацыі і апавежання пра неяксаверныя даны. Большасць экраноў у працэйным режыме — це толькі варыяцыі гэтага прыкладу: заголовак профіля плюс форма налашчэнняў, іконка кашы плюс элементы кошыка, дзвонык пра паведамленні плюс сторонняе адзінкі пра паведамленні.
Проектаванне ключа запита як шляхоў да файлам
Спрыяйце ключам як іерархічным шляхам:
['menu', restaurantId]['menu', restaurantId, 'lunch']['menu', restaurantId, 'item', itemId]['allergies', restaurantId, tableId]
Фабрыкі дапамагаюць:
Анулюванне ['menu', restaurantId] можа здійснюваць нечыткае падпадання глыбокіяшых ключоў, калі така настройка є, і самэ гэта дазволяе зберагаць спісы і деталізаваныя відображэння разам. Утримайцеся ад падмешчання несеріялізавальных значэнняў (функцый, экземпляраў класаў) унутрь ключоў. Вярніцеся да прымітывных ідэнтаў і стабільных энумерацый.
Выбір staleTime з урахоўваннем мовы продукту
Запытайце власнікаў продукту, насколькі некоректны можа быць інтерфейс пры N секунд. Маркетынгавы тэкст, який зміняецца ў кожны месцы, можа падкрэсляць длігі термін актуальнасці. Падчытанне запасоў пад час спецвыпродажаў можа выклікваць патрэбу ў майже нульовым значэнні staleTime, а таксама у скасаванні данных пасля кожной змены пакупкі. Запісайце выбор пры самой запытцы, каб будучыя рэдагувальнікі не “оптымізавалі” нестабільную запытку, ператвараючы яе ў длігі тэрмін некоректнасці.
gcTime — это параметр керавання памяцю. Мобільныя дапыткі з великай колькасцю маршрутаў выгадваюць, калі недзеявыя экраны застаюцца на памяці каротка час, ў результате навигацыя назад выглядае мгновенной. Адчынныя кэшы вельмі большых розмераў на прыстроях з малай памяцю выклікаюць патрэбу ў меншым значэнні gcTime або паградуванні.
Змены, якія здаюцца безпечнымі
Заўсёды паказвайце статусы «чакаецца» і «памылка». Выключайце кнопкі, якія можу нанесць шкоду, калі ў статусе «чакаецца». Для выдалення оптымістычная механіка выдалення должна восстанавіць знімак стану, якщо сервер адпавядае кодамі 409 чыста 500. Для стварэння оптымістычныя рядкі патрабуюць, каб тымчасовыя ідэнтыфікаторы кліента пасля успеху былі заменены на ідэнтыфікаторы сервера — або ж, калі мапаванне ідэнтыфікатораў є складным, проста анулюйце даныя.
Паралельныя змены на аднай і той жа клучоўскай вялічыне можу стварыць суперску; або вырашайце ў парады, або выключайце кантролі. Функцыя mutateAsync у бібліятэках формаў должна знаходзіцца ў обробчыках надсылкі з элементамі try/catch, а не ў функціях відраслівання.
Шаблоны анулювання, якія падтрымваюць масштабаванне
Пасля заходу ў систему неабяжна анавалідаваць ключы, прызначаны для конкрэтнага пользователя, а не чыставаць усі данні кліента, якщо трэба застаць публічны контент. Пасля выходу з системы зазвычай правільным є викорыстанне queryClient.clear(). Калі WebSocket паведамляе пра змяну меню, трэба вызваць тыя ж функціі анавалідавання, якія викорыстовуюцься пад змянамі через HTTP, ўжо таму, што обе стратегіі павінны выконвацца за адной схемай синхронізацыі.
Для верасаблівых сторонніх сторынак можна выконваць прадзейсвоўчы запит пад наведеннем курсора: queryClient.prefetchQuery({ queryKey, queryFn }) ператварае відчуваемую затрымку на успех кэшавання, не зменшаючы коду экрана.
Оптымістычныя апдэйты без міфалогіі
Оптымізм не є обавязковым для кожнага запиту типу POST. Яго трэба викорыстоўваць, калі успешны ход задачі є распашчастым, корысть для інтэрфейса ўочымаўная, а вярнэнне да пачатковага стану лёгкае. Йго трэба адклаць, калі верыфікацыя на сервере є складной або калі для адрасавання экрана неабходны данні з адпаведзення (ідэнты, цены, податкі, створаныя на сервере). Памедзліва індыкатарка можа быць кращай, чым мгновеннае паказанне некоректных дадзэнняў.
Калі вы вжываеце оптымізм, трэба заставіць кадры даных не зменявацца, анулюваць суперасунковыя запиты ў функцыі onMutate, а таксама завжды синхронізаваць даныя ў функцыі onSettled. У режыме разработкі трэба логаваць про анулювання змян; тыхе анулювання плутаюць спеціястаў з пераканальнай перапрацоўкі.
Як гэта параганяецца з глобальнымі кліентскімі хранільнямі
Redux або Zustand могу хаваць даны сервера, але тады трэба будзе ствараць кеш занова, просіць про усунення дуплікацый і адкрываць фонавыя запиты на апдэйт. TanStack Query спеціалізуецца на такіх задачах. Тымчасовую інтерфейсную частку трэба хаваць у локальным стане або маленькай кліентскай хранільні; серверныя ентытэты — у кешы запитаў. Спалучэнне гэтых струмоў ведае да дуплікацыі аднародных джэрел правды.
Навчанне каманды
Адрыхтуйце сесію навчання: створыце маленькія прыклады дапрацоўкі — меню з запитамі да спісу, деталяў, функцыямі стварэння змян, анулювання даных і оптымістычным стварэнням. Трэба выканаць вимогі ў вигляді наявнасці функцый стварэння ключоў і можлівасці выйшаць з акаунта. Калі гэты патэрн стане частым, у большых прыкладах програм будзе менше багоў, якія выступаюць у форме некоректнай роботы функцый useEffect.
Таблыца падсумку ў прозе
Запыткі чытаюцца. Мутацыі запішываюцца. Ключы называюць рядкі кэшу. staleTime адпавядае на пытанне: «Магчыма ўжо викорыстаць гэта без запиту да кухні?» gcTime адпавядае на пытанне: «Магчыма абмовіцца ад гэтай сторонкі зошыта?» Функцыя неваліднасці адпавядае на пытанне: «Кухня зменілася — апавежце». Функцыя оптымізма адпавядае на пытанне: «Апошні раз адзначыце суму, якщо будзе адхіленне — стерце». Для транспортавання вяртаецца Axios або fetch. Гэты распад працы і є весь продукт.
Сцэнарый «з начала да канца»: таблыца спецыяльных прыемакоў на обед
Рэстаран пачыная роботу ў перыяд абеда. Для запытання да таблеткі з спецыяльнасцямі выкарыстоўваецца queryKey: ['menu', restaurantId, 'lunch'] з параметрам staleTime у два мінуты, таму што таблеткі з інфармацыяй медленна меняюцца за перыяд роботы. Для віджэта з супамі выкарыстоўваецца ['menu', restaurantId, 'soup'] з періядам неактуальнасці у трыдзець секунд. Оба queryFn-ы вызываюць той самы экземпляр Axios з інтэрсэпторамі аутхентыкацыі. Калі адміністратар захоўвае новы суп через useMutation, функцыя onSuccess анулюе якія-небудзь з ключоў — або анулюе спяльны прыфікс ['menu', restaurantId], якщо намеравана ўжыцца фаззі-падобнае падабранне. Гасці на всіх актыўных таблетках бачаць апдэйты без ручнага апавяржання.
Якбы форма адміністратара выкарыстоўвала ручна створаны fetch без анулявання данных, таблеткі заставаліся бы неактуальнымі паўтарной загрузкай. Гэта неактуальнае становішча — глюк, які мяжы TanStack Query існуюць для таго, каб запобiec.
Фабрыкі ключоў запытанняў у TypeScript
Централізаванне керування клучамі:
menuKeys.all(restaurantId)menuKeys.lunch(restaurantId)menuKeys.item(restaurantId, itemId)
Фабрыкі запобегаюць памылкам у напісанні та дазволяюць шукати невалідныя элементы ў базе коду. Лепш выкорыстоўваць туплі з прымітываў. Калі існуюць фільтры, неабходна включыць серыяваныя об’екты фільтраў з стабільным порядкам клучоў. Ніколы не кладзіце цэлага об’екта параметраў з props у клуч, як толькі ён не ўрахоўваецца у мемаізацыі і є серыяваным.
Параметры useQuery, з якімі вы будете працаваць
За межамі queryKey і queryFn: параметр enabled запобегае выкарыстоўванню запытак пакуль не будуць асуществаваць ідэнтыфікаторы; retry кантролюе стратэгію рашучча з тымчасовымі адказамі; параметр refetchOnWindowFocus можа быць выключаны для дорогіх панелей керування; placeholderData або initialData спамячваюць стабільную структуру дакументаў; параметр select скарацоўвае колькасць дадзеных, якія прыймаюцца, каб зменшыць колькасць перзапісаў. Пачатковыя настройкі падходзяць; іх трэба налаштаваць па кожным запыту, калі статыстыка паказвае частыя перзапісы.
Розумэнне прынцыпу stale-while-revalidate з точкі зору UX
Паказанне сумы каляскі з вочара прыблізна на 100 мс пад час перзапісу можа быць непрыемным; паказанне статты з центру дапамогі з вочара прыблізна на мінуту ўсё нормальна. Гэта трэба задаць у параметре staleTime, а не за дапамогою спецыяльных флагаў. Адказ пра невыпаданне перзапісу на фоне не должен вычышаць правільныя, застарэлыя дадзеныя, якщо толькі вы сабе явна гэта не не паказаце; корыстнікі воляюць трохі застарэлую інформацыю, чым адказ з симвалам заглушкі пад час недзеўеры.
Мутацыі: анатамія падзеяў падачы суцэльнага формата
Абяксавайце кантакт на стадіі чакання; адобразайце памылкі ўнутры error; пасля успеху анавайце чы рэнтгенавайце кеш; пасля завершэння, як гэта прыемна, ачыстайце стан локальнага формата. Оывядзіце mutateAsync з try/catch унутры працоўніка падачы бібліятэкі формата. Не выклякайце mutate у ціклах без кантролю паралельнасці. Для аплоадаў адобразайце прыбыль окрасна — TanStack Query стежы за статусам мутацыі, а не за прыбылью байтаў.
Деталі гранулярнасці анавайвання
Занадта вузкая гранулярнасць: анаводзіце спіс, але забываеце дэталі → старыя даны на сторунцы дэталяў. Занадта шырокая гранулярнасць: кожны ключ пад ['menu'] перзапрашваецца → вялікі навантажэння. Падберайце гранулярнасць так, каб яна падходзіла да экранаў, якія можуць паказваць несупараднасці. Як толькі ёсьць сумневы, анавайце спіс і ідэнтыфікатор дэталяў, які вы зменілі. Прыменшэнне значэння ў пачатку прызначана для намеровагага распрасцэрання.
Падступы оптымістычнага апдэйта
Снімак паведамлення должен абсалютна-клонаваць дастатковую структуру, каб падтрымаць вярненне вкладаных спісоў. Тымчасовыя ідэнтыфікаторы кліента не должны трапіцца на сервер. Якщо калькуляцыі аптимістычнага типу выкананы паралельна, адвярнення можа пагубіць адзіны другога — серыялізавайте інтерфейс для такіх сцэнарыяў. Заўжды паверніцеся да правдападобных даных сервера пасля завершэння, нават якщо ўсё працюе, таму што сервер можа нормалізаваць поля, якія вы не адправілі.
React Strict Mode і двойная установка
У режыме разработкі Strict Mode двацьці разоў выкананы эфекты. TanStack Query адмахвае дублікаты па ключу, таму вы не должны бачыць двойных запытоў да сеті для таго ж ключа пад час выканання. Якщо такое відбуваецца, ваш ключ ў стабільным стане або проблемы з queryFn парадулююць прыпускі аб адмахванні дублікатоў. Логавайце ключы пад час дэбаггінгу.
SSR і прыметкі па гідратацыі
Для Next.js і падобных фреймворкаў неабходна адсушыць кліентскую частку запитоў на сервере, а пасля восстановіць яе на кліянтэ, каб нотаткі застаўаліся актуальнымі пасля переходаў. Неабходна, каб функцыі queryFn выкананыя былі ў обох сераўнах, альбо ж неабходна выкарыстоўваць серверскіе запыты прадзейсвоўвання, якія заполняюць кэш пры прадрукаванні. Неспакоўнасць у формате дадзеных між серверам і кліянтам вызывае паведамлення пра проблемы восстановлення, якія выглядаюць як багі фреймворка, але на самай працэ ўсё чыста з кэшам.
Порэванне з ручнымі патэранамі SWR
Багато команд перадумваюць часткі гэтай бібліятэкі: карты кэша, прадзейсвоўванне пад час фокусу, мутацыі і пераверыце. TanStack Query стандартызуе гэтыя падходы за дапамогою стандартных настаўкі спяльнай адументнасці і інструментаў разработчыка. Ручная реалізацыя ўсунутая толькі для маленьких прыложэнняў або экзотычных средаў выканання. У іншых случаях перавагу мае стандартны падход за рахунак часу, які не трэба витрачаць на ручную наладку.
Інструменты разработчыка і можлівасці адзоравання
Інструменты развіцця React Query паказваюць ключы, ступень застарэласці, спостерагачаў і статус запыту. Навучыце каманду чытаць іх прытымкі да запісу ў консоль. У працэйнай серыі неабходна адмахнуцца ад чутлівых дадзеных у звястках пра аберанні; калі таго трэбуе прыватнасць, логаваце аб бядах запытуў з назвамі ключоў, а не цэлымі пакетамі дадзеных.
Спіс анти-шаблонаў
Нестабільныя ключы; відсутнае анулювання дадзеных; бесканечная застарэласць без сінхронізацыі змян; запіс флагоў UI у кэш сервера; занадта шыроке анулювання дадзеных; оптымістычныя апдэйты без можласці вярнуць стан у пачатковы; ігнораванне параметра enabled пакуль не будуць ідэнтыфікаторы; викорыстоўванне мутацый для чытання дадзеных. Што бы ухіліцца ад гэтага, усе застанется ў порядку.
Короткая падсумовка для тых, хто шукае галоўныя моманты
Адміністратары транспортуюць даны. Менеджеры запам’ячваюць і координуюць дзеяння. Сторанкі зошыта для нотатак павінны мястіць назвы ключоў. Регуляторы свежасці кантролююць можлівасць павторнага викорыстання. Мутацыі запішываюць змяны. Анулювання дадзеных і оптымізм памячаюць пра чыстасць зошыта для нотатак. Гэта і є TanStack Query за адну момент — і прычына, чаму ён выкарыстоўвае Axios замест таго, каб заменіць яго.
Дадатковыя трэнінгі для кухні для практыкі
Паўторна створыце маленькія дапыт: дапыт для спісу, дапыт для детаўлявання, створыце мутацыю з анулюванням, а потым оптымістычнае стварэнне з прымусовым шляхам для адпаведных памылак. Дадаце функцію выйшчы з акаунта, якая чыстае даны на кліентскай стороне. Дадаце функцыю прадзейснення запитоў заздалегідь, калі наведзены пункт спісу. Замеры сетевых запытаў у Devtools да і пасля выкарыстоўвання спільных ключоў. Гэтыя трэнінгі краща закодуюць бібліятэку, чым проста чытанне табелей API.
Працюючы з праектамі змян, запытайце: што такое ключ? Шта такое staleTime і чаму яго выкарыстоўваюць? Чыя даны анулююцца пасля змін? Чы не блокуе чакаючая UI можлівасць двойных адправленняў? Якшыя будуць адказы, функцыя будзе праверна працаваць пад навантажэнням канкурэнціі і навігацыі.
Шаблоны прадзеявлення, які даюць вражанне мгновеннае адпрацоўкі
Адзеявляйце даныя, калі парадок на маршруце ўвесь час знаходзіцца ў фокусе, калі клавіша Tab увесь час ў фокусе прытаму, як паўзік натиснення клавішы, або пасля захаджання для стандартнага запиту дашборда. Прадзеявленне заполняе кэш без стварэння спостерагача. Калі корыстувальнік навігуе, useQuery знаходзіць вялікі за мерой даныя і прахоўвае прадзеявленне. Якщо адзеявіце некоректны ключ, вы выкорыстаеце зайвы канектаванне; якщо — правільны ключ, то спрабоўнае час адпрацоўкі падыходзіць, не змянюючы коду queryFn.
Спаўнюйце прадзеявленне разам з рэалістычным значэнням staleTime. Прадзеявленне даных, якія ведаюць стацыонарнымі мгновенна, выклікае мгновэнае прадзеявленне на фоне — гэта все ж лепш, чым пачатак ад зялёнага экрана, але не безкоштовна. Адзеявляйце даныя критычных маршрутаў; не чапайцеся за рэдкія экраны налашоўкаў.
Залежныя запиты і кантроль адпрацоўкі
Калі даклэгавам патрабуецца ідэнтыфікатор з спісу выбраных элементаў, наладзіце блокаванне за дапамою enabled: !!selectedId. Калі другы запит патрабуець данні з першага, аберагвайце ўзаемную прыяўленасць: або вкладзіце другі ключ у ідэнтыфікатор результата першага запита, або выкарыстоўваце адзін функцыю queryFn, якая вяртае оба форматы даклэгаваў, якщо API гэта падтрымлівае. Адзначытыя запісы пагаршуюць час адпаведзення; калі ёсць можлівасць, лепш выкарыстоўваць паралельныя запіты з спакойнай аутентыкацыйной інфармацыяй.
Режым Suspense зменшае спосаб складання меж зачытання даклэгаваў. Якшто каманда выкарыстоўвае Suspense, паспрабуйце супарадаваць межы адхэлень і пераканацца, што бягавыя адхэленні запітов выклікаюцца як і планавалося. Сумешанне режыму Suspense і класычных флагаў зачытання плутае тых, хто адгукваецца — выберыце адны стыль для кожной дрэва маршрутаў.
Стражынаванне, бесканечныя запіты і кешаванне сторунак
У спісах сторанакоў часта выкарыстоваюцца параметры сторанакі ў ключы: ['orders', { page, pageSize, status }]. Змена сторанакі стварае новы элемент кэшу; ўтримайце паканяныя даны за дапамогою placeholderData: keepPreviousData (або адпаведнага параметра сучаснай API), каб таблыця не паказвалася порожней. Бесканечныя запыткі дадаюць новыя сторанакі; абавязкова праверайце ўтратачыннасць даных, каб не сталося неспадзеванага скасавання пазіцыі прасування. Калі мутацыя зменяе адну строчку, падкоррекцыіяйце той элемент спісу або скасавайце утратачыннасць всіх элементаў, залежна ад чутлівасці сортавання.
Вярнэнне пасля адказаў на адказы і ўжыванне практык перапрыбутку
Стандартныя практыкі перапрыбутку дапамагаюць у ситуацыях з нестабільным мобайльным сетам. Для кода 401/403 выключыце перапрыбуткі і направьце користувача на процедуру заходу. Для кода 404 пад час адзначэння дакладных даных негайна паведамьце пра бягучую адзіну непрацэсоўнасць. Для адпаведальных корыстувачаў пакажыце значэння failureCount і failureReason у падпанелях падтрымкі. Аб’екты-слухальнікі глобальнага QueryCache можу паведамляць пра адзіну непрацэсоўнасць на кожны ключ, а не на кожнага слухальніка — так будзе адмовіцца ад множычных паведамленняў, калі пяць компонентаў выкарыстоваюць аднойчы неякі ключ.
Стратэгіі тэставання
У тэстах на елементы выкорыстоўвайце новы аб’ект QueryClient з параметрам retry: false і скорыя процедуры сбора мусора. Адтварыце макет queryFn або выкарыстоўвайце MSW. Пераканаўцеся, што статусы загрузкі, успеху і адзінакоў былі перакананы. Для мутацый пераканаўцеся, што функцыя invalidateQueries была вызвана з правільным ключам. У інтеграцыйных тэстах трэба пераканаўцяся, што два компоненты, якія выкарыстоўваюць аднойчы той самы ключ, не выкарыстоўваюць данні два разы. Нестабільныя тэсты часта вынікаюць з залишковага кэшу между тестамі — стварайце новы кліент для кожнага тэсту.
Апдэйты версій і змены API
У TanStack Query апдэйт з v4 на v5 парадзіў перайменаваннем дзеякіх опцый і зменамі стандартных настаўленняў. Пры апдэйце чытайце кансультатывныя інструкцыі, апдэйтуйце пакет Devtools і зноў перагляньце выкарыстоўванне keepPreviousData / placeholderData. Фіксуйце версіі ў файлах забаранень. Змены формы ключа запыту лячыце як проблемы: старыя кэшы можаць не падходзіць да новых ключоў пасля развяртання — прыйміце можлівасць адночасовага вялікаг кэшування або зменіце прыфікс ключа.
Запісы з інцыдэнтав у працоўным режыме
Адзіны каманды паблікаваў дуплікатныя POST-запыты, таму што кантактная кнопка заставалася актыўной, калі на іншай прымэры мутацыі значэнне isPending было правдзівым. Іншыя некалькі разоў нечакана вычысцілі весь кэш пад час выйшаўця з абліку, стварыўшы новага кліента без вычысцэння старога канектора. Іншыя кодавалі об’екты корыстніка ў ключоў і такім чынам нарабілі проблемы зі спадзяльненням структуры. Запісайце гэтыя прыклады ў документах для новак, каб яны не патраплялі на тыя ж проблемы занова.
Документавайце стандартныя настройкі вашага продакту: стандартнае значэнне staleTime, якія запыты є спецыфічныя для корыстніка, як выйшаўць з абліку вычысцвае стан, і калі дазволены оптымістычныя апдэйты. Адпаведнасць важней за хітрасць.
Запісы з інцидэтаў у працоўным режыме
Адзіны каманды паблікаваў дублікаты запытак POST, таму што кантактная кнопка заставалася актыўной, калі на іншай прымэры мутацыі значэнне isPending было правдзівым. Іншыя некалькі разоў нечакана вычысцілі весь кэш пад час выйшаўця з абліковання, стварыўшы новага кліента без вычысцэння старога канектора. Іншыя кодавалі об’екты корыстніка ў ключоў і такім чынам нарабілі проблемы зі спадзяльненням структуры. Запісайце гэтыя прыклады ў документах для новак, каб яны не патраплялі на тыя ж проблемы занова.
Документавайце стандартныя настройкі вашага продакту: стандартнае значэнне staleTime, якія запыткі є адноснымі да корыстніка, як выйшаўця з абліковання вычысцвае стан, і калі дазволены оптымістычныя апдэйты. Адноснасць важней за хітрасць.
Запісы з інцидэтаў у працоўным режыме
Адзіны каманды паблікаваў дуплікатныя POST-запыты, таму што кантактная кнопка заставалася актыўной, калі на іншай прымэры мутацыяй значэнне isPending было правдзівым. Іншыя некалькі разоў неправильна вычысцілі весь кэш пад час выйшаўця з абліковання, стварыўшы новага кліента без вычысцэння старога канектора. Іншыя кодавалі об’екты корыстніка ў ключоў і такім чынам нарабілі проблемы зі спадзяльненням структуры. Запісайце гэтыя прыклады ў документах для новачакоў, каб яны не патраплялі на тыя ж проблемы занова.
Документавайце стандартныя настройкі вашага прыстрою: стандартнае значэнне staleTime, якія запыты є спецыфічныя для корыстніка, як выйшаўця з абліковання вычысцвае стан, і калі дазволены оптымістычныя апдэйты. Адпаведнасць важней за хітрасць.