Skip to content

Bölüm 11 / 16

React Router

Rota, iç içe rota, korumalı rota.

10 dakikalık okuma

1. dersin kütüphane seçim tablosunda "router: kutuda yok, seçersin" yazıyordu — topluluğun seçimi React Router'dır ve bu ders o hücreyi kapatır. Zihin modeli 10. hattın 7. dersinden hazır: URL bir durumdur, router URL'yi bileşen ağacına çevirir. Burada React Router'ın modern (veri dönemli) API'sini kuruyoruz: createBrowserRouter, iç içe rota + Outlet, useParams/useNavigate/useSearchParams, korumalı rota — ve Angular'dan tanıdığın iki gerçeğin aynısı: SPA fallback sunucu işidir, guard güvenlik değildir. Kapanışta lazy rota, 13. dersin kod bölme konusuna köprü atar.

01Ne öğreneceksin

  • URL = durum: SPA'da rotanın ne olduğu, History API ve SPA fallback'in burada da geçerli oluşu
  • createBrowserRouter + RouterProvider; rota ağacı, errorElement, 404
  • İç içe rota + Outlet (router-outlet kıyası); Link/NavLink — <a>'dan farkı
  • useParams (parametre hep string), useNavigate, useSearchParams — "ekran adresi URL'de" ilkesi
  • Korumalı rota: 8. dersin oturum context'iyle Navigate deseni; "gizlemek UX, yetki API'de"
  • Lazy rota ve kod bölme köprüsü (13. ders); loader/action'ın adı ve yeri

02Kurulum: URL'den bileşen ağacına

Tek sayfa uygulamasında gezinme, 10. hattın 7. dersinde çözdüğün problemin aynısıdır: sayfa yeniden yüklenmez; router, History API ile adres çubuğunu değiştirir ve URL'ye bakıp hangi bileşenlerin görüneceğine karar verir. Bundan çıkan sunucu gerçeği de aynen taşınır — SPA fallback: kullanıcı /basvurular/42'yi doğrudan açtığında sunucuda böyle bir dosya yoktur; sunucu bilinmeyen yolları index.html'e düşürecek şekilde ayarlanır, gerisini tarayıcıda router çözer. Bu ayar yapılmazsa belirti klasiktir: uygulama içi gezinme çalışır, yenileme (F5) 404 verir. React Router'ın modern kurulumu rota ağacını veriyle tanımlar:

import { createBrowserRouter, RouterProvider } from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Kabuk />,                 // üstbar + yan menü + Outlet
    errorElement: <HataSayfasi />,      // render/loader hatasında bu dal görünür
    children: [
      { index: true, element: <Pano /> },              // "/" → varsayılan çocuk
      { path: "basvurular", element: <BasvuruListesi /> },
      { path: "basvurular/:id", element: <BasvuruDetay /> },   // :id = parametre
      { path: "*", element: <Bulunamadi /> },          // 404: hiçbiri eşleşmedi
    ],
  },
]);

// kökte, bir kez:
<RouterProvider router={router} />

Angular'daki Routes dizisinin birebir muadilini görüyorsun: yol desenleri, çocuk rotalar, joker. İki kıyas notu: React Router'da rota tanımı da "sadece veri"dır ama bileşenler element olarak doğrudan verilir; ve errorElement, Angular'da elle kurduğun hata sınırının rota seviyesindeki hazır hâlidir — dalda fırlayan render hatası uygulamanın tamamını düşürmez, o dalın hata sayfasını gösterir.

03İç içe rota, Outlet ve Link

Rota ağacının kilit fikri şudur: URL'nin her parçası, ekranın bir katmanına karşılık gelir. /basvurular/42 açıkken hem kabuk (üstbar + menü) hem liste sayfasının çerçevesi hem detay paneli görünebilir — çocuk rotanın bileşeni, ebeveynin Outlet'ine yerleşir:

function Kabuk() {
  return (
    <>
      <Ustbar />
      <YanMenu />
      <main>
        <Outlet />      {/* eşleşen ÇOCUK rota buraya render edilir */}
      </main>
    </>
  );
}

Outlet, Angular'daki <router-outlet>'in karşılığıdır — aynı iş, bileşen olarak. Gezinme bağlantısı da kendi bileşenini ister: <a href> tarayıcıya tam sayfa yükletir ve SPA'nın tüm state'ini sıfırlar; <Link to="/basvurular"> ise tıklamayı yakalar, History API'yi günceller, render'ı router'a bırakır. Menüler için NavLink vardır: aktif rotadayken isActive bayrağı verir, "hangi sekmedeyim" vurgusu tek satır olur:

<NavLink to="/basvurular"
  className={({ isActive }) => isActive ? "menu-baglanti aktif" : "menu-baglanti"}>
  Başvurular
</NavLink>

04Parametreler: useParams, useNavigate, useSearchParams

Rota parametresi, URL'nin dinamik parçasıdır ve okuma tarafında 4. dersin sayı tuzağıyla aynı gerçek karşılar: URL'den gelen her şey string'dir:

function BasvuruDetay() {
  const { id } = useParams();              // id: string | undefined — SAYI DEĞİL
  const sayiId = Number(id);
  if (!Number.isInteger(sayiId)) return <Bulunamadi />;   // "/basvurular/abc" savunması

  const { data, isPending, isError } = useQuery({
    queryKey: ["basvuru", sayiId],         // 10. ders: parametre anahtara girer
    queryFn: ({ signal }) => apiIstek<Basvuru>(`/basvurular/${sayiId}`, { signal }),
  });
  // ...
}

Programatik gezinme — form gönderimi sonrası detaya gitmek gibi bir olayın sonucu olan geçişler — useNavigate ile yapılır: navigate(`/basvurular/${yeni.id}`); geçmişte iz bırakmaması gereken yönlendirmelere (girişten sonra geri tuşu giriş sayfasına dönmesin) { replace: true } eklenir. Render sırasında karar verilen yönlendirme ise bileşendir: <Navigate to="..." /> — birazdan korumalı rotada göreceksin.

Üçüncü araç 9. dersin ilkesini yerine oturtur. "Ekran adresi olan durum URL'de yaşar" demiştik — filtre, sayfa numarası, seçili sekme. Aracı useSearchParams'tır:

const [arama, setArama] = useSearchParams();
const filtre = arama.get("durum") ?? "hepsi";       // ?durum=beklemede — yine string

// filtre düğmesi state'e değil URL'ye yazar:
<button onClick={() => setArama({ durum: "beklemede" })}>Beklemede</button>

Kazanç üçlüdür ve mülakat cümlesidir: link paylaşılabilir (arkadaşın aynı filtreli ekranı açar), yer imlenebilir, geri tuşuyla gezilebilir. Filtreyi useState'te tutan sürümde üçü de kaybolur; üstelik filtre zaten queryKey'e girdiği için (10. ders) URL → sorgu → ekran zinciri kendiliğinden kurulur.

05Korumalı rota: tabela, kilit değil

"Ayarlar sayfasını yalnız giriş yapmış kullanıcı görsün" ihtiyacının React Router deseni, 8. dersin oturum context'iyle birleşen bir düzen rotasıdır — yol eklemeyen, yalnız denetleyen bir ara katman:

function RotaKoruma() {
  const { kullanici } = useOturum();       // 8. dersin fırlatan hook'u
  const konum = useLocation();

  if (kullanici === null) {
    return <Navigate to="/giris" replace state={{ nereden: konum }} />;
    // nereden: girişten sonra kullanıcıyı kaldığı yere geri götürmek için
  }
  return <Outlet />;                       // yetkili: çocuk rotalar görünsün
}

// rota ağacında — path'siz düzen rotası:
{
  element: <RotaKoruma />,
  children: [
    { path: "ayarlar",  element: <Ayarlar /> },
    { path: "raporlar", element: <Raporlar /> },
  ],
}

10. hattın 7. dersindeki cümle burada kelimesi kelimesine geçerlidir ve mülakatta eleyen sorudur: guard/koruma güvenlik değildir. Bu kod istemcide çalışan JavaScript'tir; DevTools'u açan herkes onu aşabilir, API'ye doğrudan istek atabilir. Koruma UX'tir — kullanıcıyı girmemesi gereken boş ekranlardan nazikçe uzak tutar; yetki API'dedir — sunucu her istekte kimliği doğrular (mekanizması 13. hatta). "Rotayı koruyorum, veri güvende" cümlesi, 10-07'de olduğu gibi burada da yanlıştır: tabela kapıya asılır, kilit sunucudadır.

06Lazy rota ve veri API'lerinin adı

Rota, uygulamanın doğal bölme çizgisidir: raporlar sayfasının grafik kütüphanesini, oraya hiç gitmeyen kullanıcıya indirtmek israftır. React'in tembel yükleme ikilisi rotayla birleşir:

import { lazy, Suspense } from "react";
const Raporlar = lazy(() => import("./sayfalar/Raporlar"));
// import(...) = dinamik import (8. hattın 17. dersi): ayrı dosyaya bölünür, tıklanınca iner

{ path: "raporlar", element: (
    <Suspense fallback={<SayfaYukleniyor />}>
      <Raporlar />
    </Suspense>
) }

lazy bileşeni ilk gerektiğinde indirir; Suspense, indirme sürerken ne gösterileceğini söyler (rota nesnesinin lazy alanı aynı işin router'a gömülü hâlidir). Angular'daki loadComponent/@defer ile tanıştığın fikrin (10. hattın 11. dersi) React karşılığıdır. Hangi rotanın bölünmeye değdiği, paket boyutunun nasıl ölçüldüğü ve Suspense'in derinliği 13. dersin (performans) konusu — buradaki görev çizgiyi bilmek: bölme birimi rotadır.

Son bir adres bilgisi: modern React Router'da rotaya loader (rota açılmadan veriyi hazırlayan fonksiyon) ve action (form gönderimini karşılayan fonksiyon) bağlanabilir — framework tarzı kullanımın temelidir ve TanStack Query ile ensureQueryData üzerinden evlendirilebilir. Bu derste bilinçli olarak bileşen içi useQuery yolunu kurduk: önce tek model otursun; loader, "rota geçişinde spinner yerine hazır veri" optimizasyonu olarak köşede dursun — adını ve ne çözdüğünü bilmen, bugün için yeterli kıdemdir.

07Mini alıştırma — Başvuru evrenine adres ver

Yaklaşık 20 dakika · StackBlitz projene react-router-dom ekle

  1. Bu dersteki rota ağacını kur: Kabuk (üstbar + Outlet), index rotası, basvurular, basvurular/:id, joker 404. Menüyü NavLink ile kur ve aktif vurgusunu gör.
  2. <a href> ile <Link> farkını kanıtla: aynı hedefe iki bağlantı koy, birine tıklayınca konsolun/state'in sıfırlandığını (tam yükleme), diğerinde korunduğunu gözle; sonra <a>'yı sil.
  3. BasvuruDetay'da useParams + Number savunmasını kur; /basvurular/abc'yi elle açıp 404'e düştüğünü gör. Parametreyi 10. dersteki queryKey'e bağla.
  4. Filtreyi URL'ye taşı: useState'teki durum filtresini useSearchParams'a çevir; filtreli URL'yi yeni sekmede açıp aynı ekranın geldiğini, geri tuşunun filtre geçmişinde gezdiğini doğrula.
  5. RotaKoruma'yı 8. dersin oturum context'iyle kur; çıkış yapıp /ayarlar'a gitmeyi dene, /giris'e düştüğünü ve giriş sonrası state.nereden ile geri döndüğünü gör. Sonra DevTools'tan context'i elle kandırmanın mümkün olduğunu göster — "koruma UX'tir" cümlesini kendi kanıtınla yaz.
  6. Raporlar'ı lazy + Suspense'e al; DevTools → Network'te sayfa dosyasının ancak rotaya gidince indiğini gör.

08Kendini sına

SPA'da router ne yapar? "Gezinme çalışıyor ama F5 404 veriyor" belirtisini teşhis et ve çöz.

SPA'da sayfa hiç yeniden yüklenmez; router, tıklamaları yakalayıp History API ile adres çubuğunu değiştirir ve URL'yi bileşen ağacına çevirir — hangi URL parçasının hangi ekran katmanını göstereceğine rota ağacı karar verir. Belirtinin teşhisi bu modelden çıkar: uygulama içi gezinmede istek sunucuya hiç gitmez, bu yüzden çalışır; F5 ya da doğrudan açılan derin link ise sunucuya gerçek bir HTTP isteği atar ve sunucuda /basvurular/42 diye bir dosya yoktur — 404. Çözüm sunucu tarafındadır: SPA fallback — sunucu, bilinmeyen tüm yolları index.html'e düşürür; uygulama yüklenir, router URL'yi okur ve doğru ekranı kurar (API yolları bu kuralın dışında tutulur). Angular'da da birebir aynı olan bu gerçek, router'ın istemcide çalışan bir katman olduğunun en net kanıtıdır.

İç içe rota + Outlet modelini anlat; Link/NavLink neden <a>'nın yerine geçer?

Rota ağacında URL'nin her parçası ekranın bir katmanına karşılık gelir: kök rota kabuğu (üstbar, yan menü) verir, çocuk rotanın bileşeni ebeveynin Outlet'ine yerleşir — Angular'daki router-outlet'in bileşen hâli. /basvurular/42 açıkken kabuk + liste çerçevesi + detay paneli aynı anda, iç içe render edilebilir; index rotası "çocuk belirtilmediğinde görünen varsayılan"dır, errorElement dalda fırlayan hatayı o dalın hata sayfasıyla karşılar, joker * 404'ü verir. <a href> SPA'da yanlış araçtır çünkü tarayıcıya tam sayfa yükletir: uygulama baştan iner, tüm istemci state'i (form taslakları, önbellek) sıfırlanır. Link tıklamayı yakalar, History API'yi günceller ve işi router'a bırakır — sayfa yaşamaya devam eder. NavLink aynı işin menü sürümüdür: aktif rotada isActive bayrağı verir, "neredeyim" vurgusu className fonksiyonuyla tek satırda kurulur.

Filtre gibi ekran durumunu useState yerine useSearchParams'ta tutmanın üç kazancı nedir? useParams'ın tip tuzağını da anlat.

Ekran adresi olan durum — filtre, sayfa numarası, seçili sekme — URL'de yaşarsa üç şey kazanılır: link paylaşılabilir (aynı URL'yi açan herkes aynı ekranı görür), yer imlenebilir ve geri/ileri tuşlarıyla gezilebilir — useState sürümünde üçü de yoktur, link paylaşan kullanıcı varsayılan ekranı görür. Ek kazanç zincirdir: filtre zaten sorgu anahtarına (queryKey) girdiği için URL → sorgu → ekran hattı kendiliğinden kurulur; URL tek doğruluk kaynağı olur (2. dersin ilkesinin adres çubuğuna uygulanması). Tip tuzağı: URL'den gelen her şey string'dir — useParams'ın id'si string | undefined, searchParams.get'in dönüşü string | null'dur. Sayı beklenen yerde Number() + Number.isInteger savunması yazılır; /basvurular/abc gibi bozuk girişler 404'e düşürülür. Bu, 4. dersteki form input'unun "value hep string" tuzağının URL'deki kardeşidir.

Korumalı rota desenini kur ve sınırını çiz: neden güvenlik değildir? Lazy rota hangi problemi çözer ve bölme birimi neden rotadır?

Desen, yol eklemeyen bir düzen rotasıdır: RotaKoruma bileşeni oturum context'inden kullanıcıyı okur; yoksa <Navigate to="/giris" replace state={{ nereden: konum }} /> ile yönlendirir (replace: geri tuşu giriş sayfasına dönmesin; nereden: girişten sonra kaldığı yere götürmek için), varsa Outlet ile çocuk rotaları gösterir. Sınır: bu kod istemcide çalışan JavaScript'tir — DevTools'la aşılır, API'ye doğrudan istek atılır. Koruma UX'tir: kullanıcıyı anlamsız boş ekranlardan uzak tutar; yetki API'dedir: sunucu her istekte kimliği ve izni doğrular (mekanizma 13. hatta). "Rotayı korudum, veri güvende" mülakatta eleyen cümledir — doğrusu tabela/kilit ayrımıdır. Lazy rota ise ilk yükleme boyutunu çözer: lazy(() => import(...)) + Suspense, sayfanın kodunu ancak rotaya gidilince indirir. Bölme birimi rotadır çünkü kullanıcı yolculuğunun doğal sınırıdır: raporlara hiç girmeyen kullanıcı grafik kütüphanesini hiç indirmez; ölçüm ve derinlik 13. dersin konusudur.

09Sınav köşesi

Yazılıda nasıl sorulur

(1) SPA'da router ne yapar; History API'nin rolü nedir? (2) SPA fallback nedir; hangi belirtiyle kendini gösterir? (3) createBrowserRouter + RouterProvider nasıl kurulur? (4) İç içe rota ve Outlet nasıl çalışır? (5) Link ile <a> farkı nedir; NavLink ne ekler? (6) useParams hangi tipte değer döner; nasıl savunulur? (7) Hangi durum useSearchParams'ta tutulur; kazançları nelerdir? (8) Korumalı rota nasıl kurulur; neden güvenlik değildir? (9) replace ve yönlendirme sonrası geri dönüş (state.nereden) ne işe yarar? (10) Lazy rota + Suspense hangi problemi çözer?

Sözlüde nasıl anlatılır — 2 dakikalık taslak

Düzen: (1) Model — "URL bir durumdur: router History API ile adresi yönetir ve URL'yi bileşen ağacına çevirir; derin linkin çalışması için sunucuda SPA fallback şarttır — Angular'dan bildiğim gerçek." (2) Ağaç — "Rota ağacını veriyle kurarım: iç içe rotalar ebeveynin Outlet'ine yerleşir (router-outlet karşılığı), errorElement dal bazında hata sınırıdır, joker 404'ü verir; gezinme Link/NavLink iledir çünkü a etiketi SPA'yı baştan yükler." (3) Parametre — "URL'den gelen her şey string'dir: useParams'ı Number savunmasıyla okurum; paylaşılabilir ekran durumu useState'e değil useSearchParams'a gider — link paylaşılır, yer imlenir, geri tuşuyla gezilir." (4) Koruma — "Korumalı rotayı oturum context'i + Navigate ile düzen rotası olarak kurarım; ama koruma UX'tir, yetki API'de — tabela ve kilit ayrımı." (5) Kıdem vuruşu — "Kod bölme birimim rotadır: lazy + Suspense ile sayfa kodu gidilince iner; loader/action'ın ne çözdüğünü bilirim, ilk modelim bileşen içi Query'dir."