React Query Nedir? Axios ve TypeScript ile Mutation Rehberi
React Query (TanStack Query) ile Axios baseURL ayarı, login mutation, create user ve update user hook'larını TypeScript ile kurun.
React Query (TanStack Query), React uygulamalarında server state'i yönetmek için kullanılır. Gerçek projelerde bu yapı genelde Axios ile kurulan tek bir API client'ın üzerine oturur: login, create user ve update user gibi mutation hook'ları tek yerde yazılır; componentler sadece form state'i ve UI durumlarıyla ilgilenir.
React Query Nedir?
React Query, API'den gelen veriyi cache'ler, yeniden doğrular ve mutation sonrası UI'ın güncel kalmasını kolaylaştırır. Client state yerine server state'e odaklanır; yani form input'u değil, API cevabı, loading/pending durumu, hata ve cache senkronizasyonu onun alanıdır.
- Server state için cache ve yeniden doğrulama
- useQuery ile okuma, useMutation ile yazma
- Mutation sonrası invalidateQueries ile otomatik refetch
- TypeScript ile input ve response tiplerini uçtan uca koruma
Kurulum
React Query ve Axios'u birlikte kullanacağız. React Query server state'i, Axios ise HTTP client katmanını yönetecek.
npm install @tanstack/react-query axiosQueryClientProvider
Uygulamanın cache'i paylaşabilmesi için QueryClient'ı root seviyesinde bir kez oluşturup provider ile sarmalayın.
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
export function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}Axios BaseURL Ayarı
Tek bir Axios instance oluşturmak tekrar eden baseURL, header ve token ayarlarını merkezileştirir. Hook'lar artık raw fetch veya dağınık axios çağrıları yerine bu api instance'ını kullanır.
import axios from "axios";
export const api = axios.create({
baseURL: "https://api.zaferayan.com",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
export function setAuthToken(token?: string) {
if (token) {
api.defaults.headers.common.Authorization = "Bearer " + token;
return;
}
delete api.defaults.headers.common.Authorization;
}Ortak TypeScript Tipleri
Input ve response tiplerini önce ayırmak hook'ları temiz tutar. Login farklı bir response döner; user create/update ise User modeli üzerinden ilerler.
export type User = {
id: string;
name: string;
email: string;
role: "admin" | "editor" | "viewer";
};
export type LoginInput = {
email: string;
password: string;
};
export type LoginResponse = {
accessToken: string;
user: User;
};
export type CreateUserInput = {
name: string;
email: string;
role: User["role"];
};
export type UpdateUserInput = Partial<CreateUserInput>;Login Mutation
Login bir yazma işlemidir; bu yüzden useMutation ile modellenir. Başarılı olunca token'ı Axios instance'a ve localStorage'a yazabilir, kullanıcı bilgisini component tarafında kullanabilirsiniz.
import { useMutation } from "@tanstack/react-query";
import { api, setAuthToken } from "@/lib/api";
import type { LoginInput, LoginResponse } from "@/features/users/types";
export const useLogin = () => {
return useMutation({
mutationFn: (input: LoginInput) =>
api.post<LoginResponse>("/auth/login", input).then(({ data }) => data),
onSuccess: (data) => {
setAuthToken(data.accessToken);
localStorage.setItem("accessToken", data.accessToken);
},
});
};Login Formunda mutateAsync
Form submit içinde sonucu bekleyecekseniz mutateAsync'i .then/.catch ile zincirlemek daha okunaklıdır. Hata yakalama, başarı sonrası redirect veya toast gibi sıralı işleri tek akışta tutarsınız.
"use client";
import { useRouter } from "next/navigation";
import { useLogin } from "@/features/auth/useLogin";
export function LoginForm() {
const router = useRouter();
const login = useLogin();
function onSubmit(input: { email: string; password: string }) {
login
.mutateAsync(input)
.then((session) => {
router.push("/dashboard");
console.log("Hoş geldin", session.user.name);
})
.catch(() => {
console.log("Email veya şifre hatalı");
});
}
return (
<button
disabled={login.isPending}
onClick={() => onSubmit({ email: "ada@dev.com", password: "secret" })}
>
{login.isPending ? "Giriş yapılıyor..." : "Giriş yap"}
</button>
);
}User Query Key'leri
Create ve update işlemlerinden sonra hangi cache'in yenileneceğini netleştirmek için query key'leri tek yerden üretin.
export const userKeys = {
all: ["users"] as const,
lists: () => [...userKeys.all, "list"] as const,
detail: (id: string) => [...userKeys.all, "detail", id] as const,
};Create User Mutation
Yeni kullanıcı oluşturulduğunda liste cache'ini invalidate etmek yeterlidir. Böylece listeyi elle set etmeye gerek kalmadan React Query güncel veriyi yeniden çeker.
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { api } from "@/lib/api";
import { userKeys } from "./queryKeys";
import type { CreateUserInput, User } from "./types";
export const useCreateUser = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (input: CreateUserInput) =>
api.post<User>("/users", input).then(({ data }) => data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
},
});
};Update User Mutation
Update mutation'ı id ve input alır. Başarılı olunca hem detay cache'ini hem liste cache'ini invalidate ederek ekranda eski veri kalmasını engellersiniz.
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { api } from "@/lib/api";
import { userKeys } from "./queryKeys";
import type { UpdateUserInput, User } from "./types";
type UpdateUserVariables = {
id: string;
input: UpdateUserInput;
};
export const useUpdateUser = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, input }: UpdateUserVariables) =>
api.patch<User>("/users/" + id, input).then(({ data }) => data),
onSuccess: (user) => {
queryClient.invalidateQueries({ queryKey: userKeys.detail(user.id) });
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
},
});
};Formlarda Kullanımı
Basit buton veya tek aksiyonlarda mutate yeterlidir. Submit akışı içinde sonuç beklemek, sayfa yönlendirmek veya toast göstermek istiyorsanız mutateAsync'i .then/.catch ile kullanın.
const createUser = useCreateUser();
const updateUser = useUpdateUser();
createUser.mutate({
name: "Ada Lovelace",
email: "ada@dev.com",
role: "editor",
});
updateUser
.mutateAsync({
id: "user_123",
input: { role: "admin" },
})
.then((user) => {
console.log("Güncellendi", user.name);
});v5 Durum Flag'leri
TanStack Query v5'te ilk bekleme durumu için isPending kullanın. isFetching aktif fetch'i, isError hata durumunu, mutation.isPending ise yazma işleminin devam ettiğini anlatır.
- query.isPending — data henüz hazır değil
- query.isFetching — ilk yükleme veya arka plan refetch aktif
- mutation.isPending — POST/PATCH/DELETE işlemi devam ediyor
- mutation.isError — mutation hata ile sonuçlandı
Özet
React Query'yi gerçek projede okunaklı yapan şey, HTTP katmanını Axios instance'a; server state işlerini ise küçük custom hook'lara ayırmaktır. Login, create user ve update user gibi mutation'lar bu yapıda net, tipli ve tekrar kullanılabilir kalır.
Sıkça Sorulan Sorular
Bu konuda en çok merak edilenler.
Login için useQuery mi useMutation mi?
useMutation. Login server'da yeni bir oturum/token üreten yazma işlemidir; cache'lenebilir bir okuma sorgusu gibi düşünülmemelidir.
mutate mi mutateAsync mi kullanmalıyım?
Basit buton tıklaması için mutate yeterlidir. Form submit içinde sonuç bekleme, .then/.catch, redirect veya toast sıralaması gerekiyorsa mutateAsync daha uygundur.
Token'ı nerede saklamalıyım?
Örnek sadelik için localStorage kullanıyor. Daha güvenli uygulamalarda httpOnly cookie tercih edilir; bu durumda Axios instance withCredentials ile ayarlanabilir.
Create/update sonrası neden invalidateQueries var?
Mutation server'daki veriyi değiştirir. invalidateQueries ilgili cache'i stale işaretler ve React Query'nin güncel veriyi yeniden çekmesini sağlar.
TanStack Query ile React Query aynı şey mi?
Evet. React Query, TanStack Query ailesine taşındı. React paketi @tanstack/react-query olarak kullanılır.
İlgili İçerikler
Bu konuyla bağlantılı diğer infografikler.
- ReactFrontend
Next.js Nedir? Production için React Framework'ü
Next.js (App Router) ile fullstack React uygulamaları geliştirin. SSR, SSG, ISR, API routes, Server Components ve performans avantajlarının pratik rehberi.
- ReactFrontend
Zustand Nedir? Basit, Hızlı, Ölçeklenebilir State Management
Zustand ile sıfır boilerplate'e yakın global state yönetimi. Store oluşturma, selectors, async actions, persist middleware ve TypeScript desteği.
- ReactFrontend
React Router v7 Nedir? Declarative Routing Rehberi
React Router v7 ile React uygulamalarında declarative routing. BrowserRouter, Routes, Link, useNavigate, nested routes ve protected routes.
Daha fazla developer infografiği keşfedin
Yeni içerikleri kaçırmamak için ana sayfayı ziyaret edin.
Tüm infografikleri gör