Просунуті Патерни та Оптимізація
Просунуті Патерни та Оптимізація
Ви вже вмієте робити базові речі. Тепер перейдемо до технік "чорного поясу". Ці патерни допоможуть вирішити складні архітектурні завдання та оптимізувати продуктивність.
Залежні Запити (Dependent Queries)
Часта ситуація: вам треба отримати користувача, а потім отримати його проекти, використовуючи userId.
В useEffect це пекло вкладеності. В TanStack Query це елегантно робиться через опцію enabled.
// 1. Отримуємо користувача
const { data: user } = useQuery({
queryKey: ['user', email],
queryFn: () => fetchUser(email),
});
const userId = user?.id;
// 2. Отримуємо проекти ТІЛЬКИ коли є userId
const { data: projects } = useQuery({
queryKey: ['projects', userId],
queryFn: () => fetchProjects(userId),
// Запит не запуститься, поки enabled === false
enabled: !!userId,
});
Поки user завантажується, userId буде undefined, і другий запит стоятиме на паузі (status: pending, fetchStatus: idle). Як тільки user завантажиться, другий запит автоматично полетить.
Трансформація Даних (select)
Іноді бекенд повертає величезний об'єкт, а вам потрібен лише маленький шматочок або відфільтрований список. Ви можете зробити це в компоненті, але тоді це буде перераховуватися при кожному рендері.
Опція select дозволяє трансформувати дані всередині Query. Результат буде мемоізований.
const { data: userNames } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers, // Повертає User[]
// Трансформуємо User[] -> string[]
select: (users) => users.map(u => u.name),
});
select виконується тільки тоді, коли data змінилася. Це чудова оптимізація продуктивності.Initial Data (Початкові Дані)
Якщо у вас вже є дані (наприклад, ви перейшли зі списку на детальну сторінку, і у вас вже є title та summary поста), ви можете показати їх миттєво.
initialData vs placeholderData
initialData: Вважається "справжніми" даними. Вони записуються в кеш. ЯкщоstaleTimeще не вийшов, запит навіть не полетить.- Use case: Передача даних з SSR (Next.js
getServerSideProps).
- Use case: Передача даних з SSR (Next.js
placeholderData: "Фейкові" дані. Вони не записуються в кеш. Вони зникають, як тільки прийдуть справжні дані.- Use case: Показати часткові дані зі списку, поки вантажаться повні дані.
// Ми в списку (List View) вже маємо короткий опис поста
const { data: post } = useQuery({
queryKey: ['post', id],
queryFn: () => fetchPost(id),
initialData: () => {
// Шукаємо пост в кеші списку 'posts'
return queryClient.getQueryData(['posts'])?.find(p => p.id === id);
},
// Якщо ми знайшли дані, вважаємо їх свіжими 30 секунд
initialDataUpdatedAt: () => queryClient.getQueryState(['posts'])?.dataUpdatedAt,
});
Скасування Запитів (Request Cancellation)
За замовчуванням TanStack Query скасовує запит, якщо компонент демонтується або ключ змінюється (щоб не обробляти застарілу відповідь).
Щоб це працювало, ваша queryFn повинна підтримувати AbortSignal.
const fetchTodos = async ({ signal }) => {
const response = await fetch('/todos', {
// Передаємо сигнал у fetch
signal,
});
return response.json();
};
Axios підтримує це так:
const fetchTodos = async ({ signal }) => {
const response = await axios.get('/todos', {
signal,
});
return response.data;
};
Тепер, якщо користувач швидко клікає по категоріях, браузер скасує попередні HTTP-запити, заощаджуючи трафік.
Динамічні Паралельні Запити (useQueries)
useQuery не можна викликати в циклі (правила хуків). Що робити, якщо у нас є масив ID [1, 2, 3], і нам треба завантажити кожен окремо?
Використовуйте useQueries (множина).
const ids = [1, 2, 3];
const results = useQueries({
queries: ids.map(id => ({
queryKey: ['post', id],
queryFn: () => fetchPost(id),
staleTime: Infinity,
})),
});
// results — це масив результатів запитів
const isLoading = results.some(result => result.isPending);
Це дозволяє запускати довільну кількість запитів паралельно, дотримуючись правил хуків.
Пагінація та Infinite Scroll
Списки даних — це хліб і масло веб-розробки. Але завантажувати 10,000 записів одразу — погана ідея. Нам потрібна пагінація або "нескінченна прокрутка".
Архітектура та Best Practices
Ми вивчили API. Тепер поговоримо про те, як не перетворити проект на смітник. TanStack Query дає багато свободи, але "з великою силою приходить велика відповідальність".