Что в React Query облегчает разработку и создание запросов на сервер?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
React Query: облегчение разработки и работа с сервером
React Query (теперь TanStack Query) — это одна из самых мощных библиотек для управления server state в React приложениях. Она действительно облегчает разработку на порядки, убирая много boilerplate кода и решая множество проблем, с которыми обычно сталкиваются разработчики при работе с API.
Главные преимущества React Query
1. Автоматическое кэширование и синхронизация Bез React Query нужно вручную управлять состоянием, переписывать статус загрузки, ошибки. React Query всё делает автоматически:
// БЕЗ React Query - куча boilerplate
function useUserData(userId) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setData(data);
setIsLoading(false);
})
.catch(err => {
setError(err);
setIsLoading(false);
});
}, [userId]);
return { data, isLoading, error };
}
// С React Query - одна строка
function useUserData(userId) {
return useQuery({
queryKey: ["users", userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
});
}
Так как React Query кэширует результаты по queryKey, если мы снова запросим того же пользователя — данные вернутся мгновенно из кэша, без нового запроса на сервер.
2. Стейл-вайл-ривалидейт (Stale-While-Revalidate) Этот паттерн позволяет показывать закэшированные данные, пока в фоне идёт обновление:
const { data, isLoading, isFetching } = useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
staleTime: 1000 * 60 * 5, // 5 минут свежих данных
gcTime: 1000 * 60 * 10, // Удалить из кэша через 10 минут
});
// Если данные старше 5 минут, они в фоне обновляются
// Но пользователь видит старые данные до этого
return (
<div>
{data && <PostList posts={data} />}
{isFetching && <small>Обновляется...</small>}
</div>
);
3. Автоматическая инвалидация и переделение После создания или обновления данных нужно автоматически обновить кэш:
function CreatePostForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newPost) =>
fetch("/api/posts", {
method: "POST",
body: JSON.stringify(newPost),
}).then(r => r.json()),
onSuccess: () => {
// После успешного создания инвалидируем кэш списка постов
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
return (
<form onSubmit={(e) => {
e.preventDefault();
mutation.mutate({ title: "Новый пост" });
}}>
<button type="submit" disabled={mutation.isPending}>
{mutation.isPending ? "Создание..." : "Создать"}
</button>
{mutation.isError && <p>Ошибка: {mutation.error.message}</p>}
</form>
);
}
4. Автоматическая переделение при восстановлении сети Если интернет был отключен, React Query автоматически переделает запросы при восстановлении:
const QueryClientConfig = {
refetchOnWindowFocus: true,
refetchOnReconnect: true,
retryOnMount: true,
};
const queryClient = new QueryClient({
defaultOptions: {
queries: QueryClientConfig,
},
});
5. Обработка ошибок и retry логика Автоматические повторы при сетевых ошибках с экспоненциальным backoff:
const { data, error, isError } = useQuery({
queryKey: ["unreliableAPI"],
queryFn: fetchUnreliableData,
retry: 3, // Повторить 3 раза
retryDelay: attemptIndex =>
Math.min(1000 * 2 ** attemptIndex, 30000), // Экспоненциальный backoff
});
6. Фоновая переделение Автоматическое обновление данных в фоне при определённых условиях:
const { data } = useQuery({
queryKey: ["notifications"],
queryFn: fetchNotifications,
refetchInterval: 5000, // Переделять каждые 5 секунд
refetchIntervalInBackground: true, // Даже когда таб неактивен
});
7. Кэширование оптимистичных обновлений Сразу показываем изменение пользователю, но если ошибка — откатываем:
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn: updatePost,
onMutate: async (updatedPost) => {
// Отменяем текущие запросы
await queryClient.cancelQueries({ queryKey: ["posts"] });
// Сохраняем старые данные
const previousPosts = queryClient.getQueryData(["posts"]);
// Оптимистично обновляем
queryClient.setQueryData(["posts"], (old) =>
old.map(p => p.id === updatedPost.id ? updatedPost : p)
);
return { previousPosts };
},
onError: (err, newPost, context) => {
// Откатываем при ошибке
queryClient.setQueryData(["posts"], context.previousPosts);
},
});
8. Бесконечные списки и пагинация Легко реализовать infinite scroll или load more:
function InfinitePostList() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ["posts"],
queryFn: ({ pageParam }) =>
fetch(`/api/posts?page=${pageParam}`).then(r => r.json()),
initialPageParam: 1,
getNextPageParam: (lastPage, pages) =>
lastPage.hasMore ? pages.length + 1 : undefined,
});
return (
<div>
{data?.pages.map(page =>
page.posts.map(post => <Post key={post.id} post={post} />)
)}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage ? "Загружаю..." : "Загрузить ещё"}
</button>
</div>
);
}
Практические примеры из реальных проектов
Глобальное управление фильтрами с React Query:
function useFilteredPosts(filters) {
const queryClient = useQueryClient();
const { data, isLoading } = useQuery({
queryKey: ["posts", filters],
queryFn: () => fetchPosts(filters),
});
const updateFilter = useCallback((newFilters) => {
// Инвалидируем старые данные
queryClient.setQueryData(
["posts", filters],
undefined
);
}, [queryClient, filters]);
return { data, isLoading, updateFilter };
}
Почему это облегчает разработку
- Меньше кода — не нужно писать useEffect, обработку ошибок, состояния
- Лучший UX — автоматическое кэширование, переделение, обработка оффлайн сценариев
- Производительность — умное кэширование снижает нагрузку на сервер
- DevTools — отличные DevTools для отладки состояния запросов
- Надёжность — обработка граничных случаев (retry, timeout, cancellation)
React Query экономит часы разработки и делает код значительно более качественным и надёжным.