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
Navigatedeseni; "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
- Bu dersteki rota ağacını kur:
Kabuk(üstbar +Outlet),indexrotası,basvurular,basvurular/:id, joker 404. MenüyüNavLinkile kur ve aktif vurgusunu gör. <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.BasvuruDetay'dauseParams+Numbersavunmasını kur;/basvurular/abc'yi elle açıp 404'e düştüğünü gör. Parametreyi 10. derstekiqueryKey'e bağla.- Filtreyi URL'ye taşı:
useState'tekidurumfiltresiniuseSearchParams'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. RotaKoruma'yı 8. dersin oturum context'iyle kur; çıkış yapıp/ayarlar'a gitmeyi dene,/giris'e düştüğünü ve giriş sonrasıstate.neredenile 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.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."
