İçeriğe atla
devcards.space
ReactFrontend

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.

9 dakika okuma
React Query infografik kartı: server state, cache, mutation ve TypeScript konularını özetleyen görsel

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.

bash
npm install @tanstack/react-query axios

QueryClientProvider

Uygulamanın cache'i paylaşabilmesi için QueryClient'ı root seviyesinde bir kez oluşturup provider ile sarmalayın.

app/providers.tsx
tsx
"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.

lib/api.ts
ts
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.

features/users/types.ts
ts
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.

features/auth/useLogin.ts
tsx
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.

components/LoginForm.tsx
tsx
"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.

features/users/queryKeys.ts
ts
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.

features/users/useCreateUser.ts
tsx
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.

features/users/useUpdateUser.ts
tsx
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.

components/UserActions.tsx
tsx
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.

Bu konuyla bağlantılı diğer infografikler.

Daha fazla developer infografiği keşfedin

Yeni içerikleri kaçırmamak için ana sayfayı ziyaret edin.

Tüm infografikleri gör