Bu kursta, modern web uygulamaları geliştirmek için günümüzde en çok tercih edilen teknolojileri öğreneceksiniz. Eğitim, temel seviyeden başlayarak ileri seviye mimarilere kadar uzanıyor. İşte bu bölümde yer alan başlıkların detaylı açıklamaları:
Sıfırdan React ile Uygulama Geliştirme: React kütüphanesinin temel mantığını, web sayfalarını nasıl dinamik hale getirdiğini ve sıfırdan bir projeye nasıl başlanacağını öğreneceksiniz.
State Management (Durum Yönetimi) Mantığı: Bir uygulamadaki verilerin (örneğin kullanıcı girişi, sepet bilgisi) bileşenler arasında nasıl aktarıldığını ve kontrol edildiğini kavrayacaksınız.
Redux Toolkit Kullanımı: Büyük ve karmaşık uygulamalarda, tüm sayfaların ortaklaşa kullandığı verileri tek bir merkezden (Store) yönetmeyi öğreneceksiniz.
Material UI Kütüphanesi Kullanımı: Google'ın Material Design standartlarına uygun, hazır ve şık tasarım bileşenlerini (butonlar, formlar, menüler) projenize eklemeyi göreceksiniz.
Formik-YUP ile Form Doğrulama: Kullanıcı kayıt veya giriş formlarında, e-posta formatı kontrolü ya da şifre uzunluğu gibi kuralları kolayca koymayı ve yönetmeyi öğreneceksiniz.
TypeScript ile Tip Yönetimi: JavaScript diline katı kurallar ekleyerek kod yazarken hata yapmanızı engelleyen, büyük projelerde güvenliği artıran TypeScript yapısını projelere entegre edeceksiniz.
Component (Bileşen) Yapısı: React'ın en güçlü yanı olan, bir web sayfasını küçük parçalara (Header, Footer, Kartlar) bölerek tekrar tekrar kullanabilme mantığını öğreneceksiniz.
React-Router-Dom: Web sitenizde sayfa yenilenmeden "Hakkımızda", "İletişim" veya "Ürün Detay" gibi farklı sayfalara (rotalara) geçiş yapmayı öğreneceksiniz.
Firebase Kullanımı: Google'ın sunduğu bu bulut sistemi sayesinde, kendi arka plan (backend) kodunuzu yazmadan kullanıcı kayıt, giriş ve çıkış işlemlerini yapmayı öğreneceksiniz.
React, web arayüzleri oluşturmak için kullanılan bir kütüphanedir. JSX (JavaScript XML) ise, JavaScript dosyalarının içinde doğrudan HTML benzeri kodlar yazmamızı sağlar. JSX sayesinde arayüz tasarlamak çok daha hızlı ve kolay hale gelir.
Uygulama Örneği: JSX içinde bir değişkeni ekrana yazdırmak.
import React from 'react';function App() {const kullaniciAdi = "Ahmet";const yas = 10;return (<div><h1>Merhaba, {kullaniciAdi}!</h1><p>Yaşınız: {yas}</p><p>Gelecek yıl yaşınız: {yas + 1}</p></div>);}export default App;
Component, bir web sayfasındaki bağımsız, tekrar kullanılabilir arayüz parçalarıdır. Props ise, bu parçalara dışarıdan veri göndermek için kullanılan parametrelerdir.
Uygulama Örneği: Bir ürün kartı bileşeni oluşturup, bu bileşene Props ile farklı ürün bilgileri göndermek.
// UrunKarti adında bir bileşen (Component) oluşturuyoruzfunction UrunKarti(props) {return (<div style={{ border: '1px solid gray', padding: '10px', margin: '10px' }}><h3>{props.urunAdi}</h3><p>Fiyat: {props.fiyat} TL</p></div>);}// Ana uygulama içinde bu bileşeni farklı verilerle (Props) kullanıyoruzfunction App() {return (<div><h2>Alışveriş Listesi</h2><UrunKarti urunAdi="Mavi Tişört" fiyat={250} /><UrunKarti urunAdi="Spor Ayakkabı" fiyat={1200} /></div>);}export default App;
useState: React'te dinamik olarak değişen ve değiştiğinde sayfanın yeniden çizilmesini (render) sağlayan verileri tutar.
useEffect: Sayfa ilk açıldığında veya belirli bir veri değiştiğinde otomatik olarak çalışmasını istediğimiz kodları (örneğin internetten veri çekme) buraya yazarız.
Uygulama Örneği: Sayıcı uygulaması ve sayfa ilk açıldığında ekrana mesaj basma.
import React, { useState, useEffect } from 'react';function SayacUygulaması() {// sayi adında bir durum (state) oluşturduk, ilk değeri 0const [sayi, setSayi] = useState(0);// Sayfa ilk yüklendiğinde bir kere çalışıruseEffect(() => {console.log("Sayac uygulaması başarıyla yüklendi!");}, []);return (<div style={{ textAlign: 'center', marginTop: '20px' }}><h1>Sayaç: {sayi}</h1><button onClick={() => setSayi(sayi + 1)}>Arttır</button><button onClick={() => setSayi(sayi - 1)}>Azalt</button></div>);}export default SayacUygulaması;
Uygulamalarımızın dış dünyayla veya bir veri tabanıyla (API) iletişim kurmasını sağlar. Veri çekmek (GET), yeni veri eklemek (POST), veriyi güncellemek (PUT) ve silmek (DELETE) için Axios kütüphanesi kullanılır.
Uygulama Örneği: İnternetten (sahte bir API'den) kullanıcı listesini çekip ekrana basmak.
import React, { useState, useEffect } from 'react';import axios from 'axios';function KullaniciListesi() {const [kullanicilar, setKullanicilar] = useState([]);useEffect(() => {// API'den verileri çekiyoruz (GET İsteği)axios.get('https://typicode.com').then(response => {setKullanicilar(response.data); // Gelen veriyi state içine attık}).catch(error => {console.error("Veri çekilirken hata oluştu:", error);});}, []);return (<div><h2>Kullanıcılar</h2><ul>{kullanicilar.map(kullanici => (<li key={kullanici.id}>{kullanici.name} - {kullanici.email}</li>))}</ul></div>);}export default KullaniciListesi;
Uygulama büyüdükçe verileri Props ile sürekli oradan oraya taşımak zorlaşır. Redux Toolkit, tüm bileşenlerin doğrudan erişebileceği merkezi bir veri deposu (Store) kurar.
Uygulama Örneği: Sepete ürün ekleme mantığının Redux ile tasarlanması.
// sepetSlice.js (Redux yapısının kurulması)import{createSlice}from'@reduxjs/toolkit';constsepetSlice= createSlice({name:'sepet',initialState: {urunler: [],toplamTutar:0},reducers: {sepeteEkle: (state,action) => {state.urunler.push(action.payload);state.toplamTutar += action.payload.fiyat;}}});exportconst{sepeteEkle} = sepetSlice.actions;exportdefaultsepetSlice.reducer;
Web sitemizde birden fazla sayfa varmış gibi davranılmasını sağlar. Kullanıcı üst menüden bir linke tıkladığında, sayfa tamamen yenilenmeden sadece o sayfanın içeriği yüklenir.
Uygulama Örneği: Ana sayfa ve Hakkımızda sayfaları arasında geçiş köprüsü kurmak.
import React from 'react';import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';function Anasayfa() { return <h1>Ana Sayfaya Hoş Geldiniz</h1>; }function Hakkimizda() { return <h1>Biz Kimiz? Hakkımızda Sayfası</h1>; }function App() {return (<BrowserRouter><nav><Link to="/">Ana Sayfa</Link> | <Link to="/hakkimizda">Hakkımızda</Link></nav><Routes><Route path="/" element={<Anasayfa />} /><Route path="/hakkimizda" element={<Hakkimizda />} /></Routes></BrowserRouter>);}export default App;
JavaScript esnek bir dildir fakat bu durum büyük projelerde hata yapmayı kolaylaştırır. TypeScript, yazdığımız verilere zorunlu tipler koyarak hataları daha kod çalışmadan yakalar.
Uygulama Örneği: Bir fonksiyonun alacağı parametrelerin tipini zorunlu kılmak.
// TypeScript ile bir objenin yapısını (tipini) tanımlıyoruzinterfaceGörev{id:number;baslik:string;tamamlandi mi:boolean;}// Fonksiyonumuzun sadece bu tipte veri kabul etmesini sağlıyoruzfunctionGorevGoster(yeniGorev: Görev) {console.log(`Görev: ${yeniGorev.baslik}, Durum: ${yeniGorev.tamamlandi mi}`);}// Doğru Kullanım:GorevGoster({id:1,baslik:"React çalış", tamamlandi mi:false});// Hatalı Kullanım (TypeScript burada altını kırmızı çizer ve uyarır):// GorevGoster({ id: "bir", baslik: "Hatalı görev" }); // id numara olmalıydı!
Kullanıcıların doldurduğu formları kontrol etmek zordur. Formik formun yönetimini kolaylaştırırken, YUP ise doğrulama kurallarını (şifre en az 6 karakter olsun, e-posta geçerli olsun vb.) belirlememizi sağlar.
Uygulama Örneği: Basit bir kayıt formu kuralları tasarımı.
import*asYupfrom'yup';// Kayıt formu için geçerlilik kuralları hazırlıyoruzconstkayitSemasi= Yup.object().shape({eposta: Yup.string().email('Geçersiz e-posta adresi!').required('E-posta zorunludur!'),sifre: Yup.string().min(6,'Şifre en az 6 karakter olmalıdır!').required('Şifre zorunludur!')});
Web sitemizi güzelleştirmek için sıfırdan CSS yazmak yerine, Material UI kütüphanesinin hazır, modern ve profesyonel buton, kart, menü (Appbar), kutu (Box) ve tablo bileşenlerini kullanırız.
Uygulama Örneği: MUI ile şık bir buton ve açılır kapanır Akordeon (Accordion) yapımı mantığı.
import React from 'react';import Button from '@mui/material/Button';import Accordion from '@mui/material/Accordion';import AccordionSummary from '@mui/material/AccordionSummary';import AccordionDetails from '@mui/material/AccordionDetails';function MuiTasarim() {return (<div style={{ padding: '20px' }}>{/* MUI Hazır Butonu */}<Button variant="contained" color="primary">Kaydet</Button>{/* MUI Hazır Akordeon Menüsü */}<Accordion style={{ marginTop: '20px' }}><AccordionSummary><h3>Sıkça Sorulan Sorular</h3></AccordionSummary><AccordionDetails><p>Bu kurs güncel mi? Evet, 2026 yılına uygundur.</p></AccordionDetails></Accordion></div>);}export default MuiTasarim;
Firebase, arka plan kodları yazmadan hazır fonksiyonlar kullanarak güvenli bir şekilde kullanıcı kaydetmeyi (Sign Up), giriş yaptırmayı (Login) ve çıkış yaptırmayı (Sign Out) sağlar.
Uygulama Örneği: Firebase ile e-posta ve şifre kullanarak giriş yapma fonksiyonu mantığı.
import{getAuth,signInWithEmailAndPassword}from"firebase/auth";constauth= getAuth();functionKullaniciGiris(eposta,sifre) {signInWithEmailAndPassword(auth, eposta, sifre).then((userCredential) => {console.log("Giriş başarılı!", userCredential.user);// Başarılı olunca ana sayfaya yönlendirme yapılır}).catch((error) => {console.error("Giriş başarısız! Hata kodu:", error.message);});}
Kurs boyunca öğrendiğiniz bu teorik bilgileri pekiştirmek amacıyla sıfırdan şu projeleri geliştireceksiniz:
Kurslarım Projesi: Kart
tasarımları yapmayı, dizi içerisindeki kurs verilerini map
fonksiyonu ile ekranda listelemeyi ve temel komponent mimarisini
kavramayı sağlar.
Döviz Kuru Projesi: İnternetteki canlı bir döviz API'sine bağlanarak TL, Dolar ve Euro arasında anlık hesaplamalar yapan bir finans aracı geliştirmenizi sağlar.
Todo (Yapılacaklar Listesi) Projesi: Listeye yeni bir görev ekleme, listeleme, görevi silme ve güncelleyerek düzenleme (CRUD) adımlarının state ile nasıl yönetildiğini öğretir.
Redux + TypeScript Todo Projesi: Aynı yapılacaklar listesi uygulamasını, bu sefer kurumsal standartlara uygun şekilde hem katı tip güvenliği (TypeScript) hem de merkezi yönetim (Redux) kullanarak baştan inşa etmenizi sağlar.
Gelişmiş E-Ticaret Projesi: Kursun en büyük projesidir. İçerisinde sepet işlemleri (ürün ekleme/çıkarma), sepet tutarı hesaplama, ürün detay sayfaları, kategoriye göre filtreleme, yerel hafıza (localStorage) kullanımı, Formik form doğrulamaları ve kullanıcı giriş sistemleri barındıran tam donanımlı bir alışveriş sitesi tasarlayacaksınız.
=====================
Kurs içerisindeki projelerin tamamı, modern bir React yazılımcısının gerçek iş hayatında karşılaşacağı senaryolar üzerine kurulmuştur. Bu projeler basitten karmaşığa doğru ilerleyerek bilgileri pekiştirir.
Aşağıda kurstaki 5 projenin de kod mimarisini, klasör yapılarını ve veri akışlarını en ince ayrıntısına kadar inceleyebilirsiniz.
Bu proje, React'ın en temel yapı taşı olan "Bileşenlere Bölme (Componentization)" ve "Props ile Veri Taşıma" mantığını öğretir.
src/├── components/│ ├── Header.jsx // Sayfa başlığı ve menü│ ├── KursListesi.jsx // Kartları döngüye sokan kapsayıcı│ └── KursKarti.jsx // Tek bir kursun tasarımı├── data.js // Kurs bilgilerini içeren statik dizi├── App.jsx // Ana bileşen└── index.css // Kartların yan yana dizilmesini sağlayan CSS
data.js
dosyasında kursların başlığı, açıklaması, fiyatı ve resim
yolu gibi bilgileri içeren bir nesne dizisi (Array of Objects)
bulunur.
App.jsx
bu veriyi içeri aktarır (import) ve KursListesi
bileşenine aktarır.
KursListesi.jsx
içinde JavaScript'in .map()
fonksiyonu kullanılarak her bir kurs verisi için bir KursKarti
oluşturulur.
Her karta özel bilgiler Props
yoluyla aktarılır. React'ın listeleri hızlı güncelleyebilmesi
için her karta benzersiz bir key
(id) verilir.
// KursListesi.jsx içindeki temel döngü mimarisiimport KursKarti from './KursKarti';import { kurslarVerisi } from '../data';function KursListesi() {return (<div className="kurs-listesi-container">{kurslarVerisi.map((kurs) => (<KursKarti key={kurs.id} kursBilgisi={kurs} />))}</div>);}
Bu proje, dış dünyadaki gerçek bir sunucuyla (API) asenkron iletişim kurmayı ve form girdilerini anlık olarak işlemeyi öğretir.
src/├── components/│ └── DovizCevirici.jsx // Input ve Select kutularının olduğu ana arayüz├── App.jsx└── index.css
Kullanıcı
miktar girdikçe (Miktar Inputu) veya para birimini değiştirdikçe
(USD, EUR, TRY) onChange
olayları tetiklenir.
Bu girdiler
useState
kancaları (hooks) ile hafızada tutulur.
"Çevir"
butonuna basıldığında veya girdiler değiştikçe Axios
kütüphanesi devreye girer. Free Currency API adresine bir GET
isteği gönderilir.
Gelen güncel kur oranı,
kullanıcının girdiği miktarla çarpılarak sonuç ekranına
(useState
ile güncellenerek) yansıtılır.
// DovizCevirici.jsx içindeki HTTP istek mimarisiimport { useState } from 'react';import axios from 'axios';function DovizCevirici() {const [miktar, setMiktar] = useState(1);const [fromCurrency, setFromCurrency] = useState('USD');const [toCurrency, setToCurrency] = useState('TRY');const [sonuc, setSonuc] = useState(0);const dovizCevir = async () => {// API'ye istek atma ve asenkron bekleme (Await)const response = await axios.get(`https://freecurrencyapi.com{fromCurrency}`);const karsilikOran = response.data.data[toCurrency];setSonuc(miktar * karsilikOran);};}
Bir yazılımcının mutlaka
yapması gereken, veri ekleme, okuma, güncelleme ve silme (CRUD)
döngüsünün saf React state (useState)
ile yönetildiği projedir.
src/├── components/│ ├── TodoCreate.jsx // Yeni görev ekleme alanı│ ├── TodoList.jsx // Görevleri listeleyen alan│ └── Todo.jsx // Tek bir görev satırı (Silme ve Düzenleme butonları içerir)├── App.jsx // Tüm görevlerin (todos state) tutulduğu ana merkez└── index.css
State
Lifting Up (Durumu Yukarı Taşıma): Görevlerin listesi
App.jsx
içinde tutulur. Çünkü hem ekleme bileşeni (TodoCreate)
hem de listeleme bileşeni (TodoList)
bu veriye ihtiyaç duyar.
TodoCreate
bileşeni yeni bir görev yazdığında, App.jsx
içindeki setTodos
fonksiyonunu tetikler ve listeye yeni eleman eklenir.
Todo.jsx
içindeki "Sil" butonuna basıldığında, görevin id
değeri App.jsx'e
geri gönderilir ve .filter()
metodu ile o eleman listeden atılır.
"Güncelle" butonuna
basıldığında satır bir input
alanına dönüşür (Edit Modu). Kullanıcı yazıyı değiştirip
kaydettiğinde .map()
metodu ile ilgili görev güncellenir.
// App.jsx içindeki CRUD fonksiyon mimarisiconst[todos,setTodos] = useState([]);consttodoEkle= (yeniTodo) => {setTodos([...todos, yeniTodo]);// State'e güvenli ekleme};consttodoSil= (id) => {setTodos(todos.filter(todo => todo.id !== id));// Listeden temizleme};consttodoGuncelle= (id,yeniYazi) => {setTodos(todos.map(todo => todo.id === id ? { ...todo,title: yeniYazi } : todo));};
Bir önceki Todo projesinin aynısıdır, fakat bu kez kurumsal büyük projelerde kullanılan katı tip güvenliği (TypeScript) ve merkezi depo (Redux Toolkit) mimarisiyle baştan tasarlanır.
src/├── app/│ └── store.ts // Redux'ın merkezi veri deposu configuration ayarı├── features/│ └── todoSlice.ts // Görevlerin durumları ve fonksiyonları (Slice)├── types/│ └── todoTypes.ts // TypeScript interface ve tiplerinin tanımları├── components/│ ├── TodoCreate.tsx│ ├── TodoList.tsx│ └── Todo.tsx├── App.tsx└── main.tsx
App.tsx
artık kendi içinde bir state tutmaz, içi boşalır ve sadece
bileşenleri çağırır. Tüm veriler store.ts
dosyasında toplanır.
todoSlice.ts
içinde yapılacak işlerin ilk durumu (initialState)
ve bunları değiştirecek fonksiyonlar (reducers)
yazılır.
Bileşenler
veriyi çekmek için useSelector
kancasını, bir aksiyonu (silme, ekleme) tetiklemek için ise
useDispatch
kancasını kullanır.
TypeScript sayesinde
fonksiyonların hangi parametreleri alacağı (örneğin id'nin
mutlaka number
olması gerektiği) zorunlu tutulur.
// todoSlice.ts içindeki TypeScript ve Redux Toolkit mimarisiimport{createSlice,PayloadAction}from'@reduxjs/toolkit';import{Todo,TodoInitialState}from'../types/todoTypes';constinitialState: TodoInitialState = {todos: [] };exportconsttodoSlice= createSlice({name:'todo',initialState,reducers: {addTodo: (state,action: PayloadAction<Todo>) => {state.todos.push(action.payload);// Redux Toolkit arka planda state'i güvenle günceller},removeTodo: (state,action: PayloadAction<number>) => {state.todos = state.todos.filter(todo => todo.id !== action.payload);}}});
Kursun tepe noktası olan bu proje; TypeScript, Redux Toolkit, React Router Dom, Material UI, Formik/YUP ve Sahte Sunucu (json-server) teknolojilerinin hepsini tek bir potada eritir.
src/├── app/│ └── store.ts // Tüm uygulamanın merkezi deposu├── slices/│ ├── productSlice.ts // Ürünleri API'den çekme ve listeleme durumları│ ├── cartSlice.ts // Sepete ekleme, çıkarma, localStorage kaydı ve fiyat hesabı│ └── userSlice.ts // Giriş yapmış kullanıcı bilgileri (Auth)├── components/│ ├── Navbar.tsx // Kategori menüleri ve sepet ikonu (Badge ile)│ ├── ProductCard.tsx // Ürün kartı tasarımı (Material UI Card)│ └── CartDrawer.tsx // Ekranın sağından açılan sepet sepet detay paneli (MUI Drawer)├── pages/│ ├── Home.tsx // Ana sayfa (Ürünlerin listelendiği yer)│ ├── ProductDetail.tsx // useParams ile ID'ye göre açılan ürün detay sayfası│ ├── Login.tsx // Formik/YUP ile hazırlanan giriş sayfası│ └── Register.tsx // Kayıt olma sayfası├── App.tsx // Rotaların (Routes) tanımlandığı ana dosya└── main.tsx
Asenkron
Veri Yükleme (AsyncThunk & Loading): Ürünler
productSlice.ts
içinde createAsyncThunk
kullanılarak API'den çekilir. Veri internetten gelirken ekranda
dalgalanma efekti (Material
UI Skeleton veya Progress)
gösterilir. Yükleme bitince (fulfilled)
ürünler ekrana basılır.
Dinamik
Sayfalama (Routing & useParams): Kullanıcı bir ürün
kartına tıkladığında useNavigate
ile /product-detail/5
gibi bir adrese yönlendirilir. ProductDetail.tsx
sayfası açıldığında adresteki 5
sayısını useParams
ile yakalar. Redux store'a giderek sadece 5 id'li ürünün
detaylarını ekrana basar.
Kalıcı
Sepet Hafızası (LocalStorage): Kullanıcı bir ürünü sepete
eklediğinde cartSlice
içindeki sepet dizisine bu ürün eklenir. Aynı zamanda bu
fonksiyon sepetin son halini tarayıcının localStorage
hafızasına kaydeder. Sayfa yenilense bile sepet sıfırlanmaz,
hafızadan geri yüklenir.
Güvenli Form Doğrulamaları:
Giriş ve kayıt sayfalarında Formik
formun gönderilme sürecini kontrol ederken, YUP
şeması girilen verileri arka planda denetler. Hatalı e-posta veya
kısa şifre girilirse Material UI TextField
bileşenlerinin altı anında kızarır ve hata mesajı basılır.
// cartSlice.ts içindeki sepet ve toplam tutar hesaplama mimarisiimport{createSlice,PayloadAction}from'@reduxjs/toolkit';constinitialState= {cartItems: JSON.parse(localStorage.getItem('sepet') ||'[]'),// Hafızadan geri yüklemetotalAmount:0};exportconstcartSlice= createSlice({name:'cart',initialState,reducers: {addToCart: (state,action) => {// Ürün zaten sepette var mı kontrolüconstfindItem= state.cartItems.find((item) => item.id === action.payload.id);if(findItem) {findItem.quantity +=1;// Varsa miktarını arttır}else{state.cartItems.push({ ...action.payload,quantity:1});// Yoksa yeni ekle}// Toplam tutarı yeniden hesaplastate.totalAmount = state.cartItems.reduce((total,item) => total + (item.price * item.quantity),0);// LocalStorage güncellelocalStorage.setItem('sepet', JSON.stringify(state.cartItems));}}});
=========================================
Bu bölümde, kurumsal seviyedeki bir React + TypeScript + Redux Toolkit projesinin kalbini oluşturan en önemli mimari yapıları üç ana başlık altında en derin detaylarıyla inceleyeceğiz.
createAsyncThunk
ile API'den Asenkron Veri Çekme Mimarisi
React bileşenleri içinde doğrudan
fetch
veya axios
kullanmak, büyük projelerde kod kalabalığına ve durum
yönetiminin (loading, error) dağılmasına neden olur. Redux
Toolkit, asenkron (internet üzerinden veri çekme gibi zaman alan)
işlemleri merkezi bir standartla yönetmek için createAsyncThunk
yapısını sunar.
Bileşen
(Component): Sayfa açıldığında bir asenkron aksiyon
fırlatır (dispatch(getProducts())).
Thunk (Ara Yazılım): Arka planda API'ye istek (HTTP Request) atar ve yanıtı bekler.
ExtraReducers (Durum
Yakalayıcı): İsteğin o anki durumuna göre (pending,
fulfilled,
rejected)
merkezi state
yapısını günceller.
productSlice.ts)import{createSlice,createAsyncThunk}from'@reduxjs/toolkit';importaxiosfrom'axios';// 1. Ürün Nesnesinin Tip TanımıexportinterfaceProduct{id:number;title:string;price:number;description:string;image:string;}// 2. Product Slice İçin State Tip TanımıinterfaceProductState{products: Product[];loading:boolean;error:string|null;}constinitialState: ProductState = {products: [],loading:false,error:null,};// 3. Asenkron Thunk Aksiyonu Tanımlama// İlk parametre aksiyonun adıdır, ikinci parametre asenkron fonksiyondur.exportconstfetchProducts= createAsyncThunk('products/fetchProducts',async(_,thunkAPI) => {try{constresponse=awaitaxios.get<Product[]>('https://fakestoreapi.com');returnresponse.data;// Bu veri 'fulfilled' durumunda action.payload olur.}catch(error:any) {returnthunkAPI.rejectWithValue(error.message ||'Ürünler yüklenirken bir hata oluştu.');}});// 4. Slice Tanımı ve ExtraReducers KullanımıexportconstproductSlice= createSlice({name:'products',initialState,reducers: {},// Senkron aksiyonlar buraya yazılırextraReducers: (builder) => {builder// İstek başladı: Loading durumunu true yap, eski hataları temizle.addCase(fetchProducts.pending, (state) => {state.loading =true;state.error =null;})// İstek başarıyla tamamlandı: Veriyi kaydet, loading'i kapat.addCase(fetchProducts.fulfilled, (state,action) => {state.loading =false;state.products = action.payload;// API'den gelen dizi})// İstek başarısız oldu: Hatayı kaydet, loading'i kapat.addCase(fetchProducts.rejected, (state,action) => {state.loading =false;state.error = action.payloadasstring;});},});exportdefaultproductSlice.reducer;
Home.tsx)importReact, {useEffect}from'react';import{useDispatch,useSelector}from'react-redux';import{fetchProducts}from'../slices/productSlice';import{RootState,AppDispatch}from'../app/store';functionHome() {// TypeScript projelerinde normal useDispatch yerine özel tiplendirilmiş AppDispatch kullanılırconstdispatch= useDispatch<AppDispatch>();// Merkezi depodan verileri çekmeconst{products,loading,error} = useSelector((state: RootState) => state.product);useEffect(() => {dispatch(fetchProducts());// Sayfa yüklenince asenkron isteği tetikle}, [dispatch]);if(loading)return<div>Ürünler yükleniyor (Skeleton Animasyonu)...</div>;if(error)return<div>Hata meydana geldi: {error}</div>;return(<div className="product-grid">{products.map((product) => (<div key={product.id} className="card"><img src={product.image} alt={product.title} /><h3>{product.title}</h3><p>{product.price} TL</p></div>))}</div>);}
Redux Toolkit mimarisinin temel amacı, verilerin tek yönlü bir akış (Unidirectional Data Flow) içinde hareket etmesini sağlayarak uygulamanın durumunu tahmin edilebilir kılmaktır. Bu üçlü yapı sürekli bir döngü içinde birbiriyle konuşur.
┌─────────────────────────────────────────────────────────┐│ │▼ │┌─────────────────┐ Fırlatır (Postacı) ┌────────────────────┐│ Bileşen (View) ├─────────────────────────────>│ Dispatch (Action) │└─────────────────┘ └──────────┬─────────┘▲ ││ Güncellenmiş Veriyi Seçer (useSelector) │ Taşır ve Tetikler│ │┌─────────────────┐ Durumu Günceller ┌──────────▼─────────┐│ Store (Depo) │<─────────────────────────────┤ Slice (Reducer) │└─────────────────┘ └────────────────────┘
Uygulamanın belirli bir özelliğine (örneğin sepet, kullanıcı, ürünler) ait verilerin yapısını ve bu verileri nasıl değiştireceğini bilen akıllı modüllerdir. Hem aksiyon üretir hem de durum yönetimini (Reducer) kendi içinde barındırır.
Bileşenlerin, merkezi depodaki
veriyi doğrudan değiştirme yetkisi yoktur. Bir değişiklik yapmak
istediklerinde bir emir (Action) vermek zorundadırlar. dispatch()
fonksiyonu, bileşenin verdiği bu emri alır ve Redux mekanizmasına
fırlatır.
Projedeki tüm Slice yapılarının bir araya gelerek oluşturduğu tek ve merkezi veri ambarıdır. Uygulamanın anlık tüm hafızası burada saklanır.
Adım (Bileşen): Kullanıcı
"Sepete Ekle" butonuna basar. Bileşen, sepet Slice'ından
çıkan addToCart
aksiyon emrini oluşturur ve postacıya verir:
dispatch(addToCart(secilenUrun));Adım
(Döngü): dispatch
bu emri alır, içinde taşınan veriyi (payload:
secilenUrun) ilgili Slice'a ulaştırır.
Adım
(Slice): Slice içindeki addToCart
fonksiyonu tetiklenir. Gelen ürünü mevcut sepet dizisine güvenli
bir şekilde ekler.
Adım (Store & Re-render):
Store güncellenir. Store içindeki sepet verisini useSelector
ile dinleyen tüm bileşenler (örneğin sağ üstteki Sepet İkonu
Sayısı) otomatik olarak kendini yeniden çizer (render) ve yeni
sayıyı gösterir.
type)
ve Arayüz (interface)
Tanımlamaları
TypeScript'te nesnelerin,
fonksiyonların ve bileşenlerin hangi veri yapısına sahip olması
gerektiğini belirtmek için iki temel araç kullanılır: interface
ve type.
interface
ve type
Arasındaki Farklar Nelerdir?interface
(Arayüz): Genellikle nesnelerin (object) yapılarını
tanımlamak için kullanılır. Nesne tabanlı programlamaya (OOP)
uygundur. Sonradan genişletilebilir (Aynı isimle tekrar
tanımlanırsa özellikleri birleştirir).
type
(Tip Takısı): Nesnelerin yanı sıra tekil değerler,
fonksiyonlar, birlikler (Union) veya ilkel tipler için kullanılır.
Sonradan doğrudan genişletilemez, birleştirme operatörleri (&
veya |)
kullanılır.
interface
ile Veri ve Model Yapılarını Tanımlama
Büyük projelerde API modelleri
veya kullanıcı verileri için interface
tercih edilir. extends
anahtar kelimesiyle başka bir arayüzden miras alabilir.
// Temel Kullanıcı ArayüzüinterfaceUser{id:string;name:string;:string;}// Admin Kullanıcısı (Normal kullanıcının tüm özelliklerine sahip + ekstra yetki barındırır)interfaceAdminUserextendsUser {role:'superadmin'|'manager';permissions:string[];}// Kullanım ÖrneğiconstaktifYonetici: AdminUser = {id:"usr_102",name:"Can",:"can@firma.com",role:"manager",permissions: ["delete_product","edit_user"]};
type
ile Esnek Birlikler (Union) ve Fonksiyon Tipleri Tanımlama
Bir verinin sadece belirli
değerleri almasını istiyorsak veya fonksiyonların girdi/çıktı
kurallarını belirleyeceksek type
kullanmak çok daha pratiktir.
// Birlik (Union) Tipi Tanımlama: Sipariş durumu sadece bu 3 yazıdan biri olabilirtypeOrderStatus ='hazirlaniyor'|'kargoda'|'teslim-edildi';// Fonksiyon Tipi Tanımlama: İki sayı alıp geriye sayı döndüren bir fonksiyon şablonutypeMatematikFonksiyonu = (a:number,b:number) =>number;consttoplamaIslemi: MatematikFonksiyonu = (x,y) => x + y;// Nesne Tiplerini Birleştirme (Intersection)typeKisi = {isim:string};typeCalisan = Kisi & {maas:number};constpersonel: Calisan = {isim:"Ayşe",maas:45000};
Bileşenlerimize dışarıdan veri gönderirken hata yapmamak için Props yapılarını tiplendiririz.
importReactfrom'react';// Bileşenin alacağı Props yapısını tanımlıyoruzinterfaceCustomButtonProps{label:string;color?:'primary'|'secondary';// Soru işareti (?) bu özelliğin zorunlu olmadığını (opsiyonel) belirtironClick: () =>void;// Parametre almayan ve bir şey döndürmeyen fonksiyon tipi}// React.FC (Functional Component) kullanarak bileşene tipleri bağlıyoruzexportconstCustomButton: React.FC<CustomButtonProps> = ({label,color='primary',onClick}) => {return(<button className={`btn btn-${color}`} onClick={onClick}>{label}</button>);};
==========================================================
Bu bölümde, istekleriniz doğrultusunda iki ana aşamayı ele alacağız. İlk olarak, tarayıcıda doğrudan çalıştırabileceğiniz ya da kendi geliştirme ortamınıza yapıştırabileceğiniz, sahte verilerle çalışan eksiksiz bir TypeScript + Redux Toolkit mini projesini tek parça halinde oluşturacağız. Ardından, Formik ve YUP ikilisinin TypeScript projelerinde form doğrulaması için nasıl güvenle kurulduğunu pratik bir örnekle inceleyeceğiz.
Bu kod bloğunda; veri modellerinin tiplerini, merkezi Store ayarını, verileri yöneten Slice yapısını ve bunları kullanan React Bileşenini tek bir dosyada bir araya getirdik. Bu senaryoda sahte bir "Kitaplık Yönetim Sistemi" kurulmuştur.
importReactfrom'react';import{configureStore,createSlice,PayloadAction}from'@reduxjs/toolkit';import{Provider,useDispatch,useSelector}from'react-redux';// ==========================================// 1. TYPESCRIPT TIP TANIMLAMALARI (TYPES)// ==========================================exportinterfaceKitap{id:string;baslik:string;yazar:string;okundu:boolean;}interfaceKitaplikState{kitaplar: Kitap[];}// Sahte Başlangıç VerilericonstinitialState: KitaplikState = {kitaplar: [{id:'1',baslik:'Nutuk',yazar:'Mustafa Kemal Atatürk',okundu:true},{id:'2',baslik:'Küçük Prens',yazar:'Antoine de Saint-Exupéry',okundu:false},]};// ==========================================// 2. REDUX TOOLKIT SLICE TANIMLAMASI// ==========================================constkitaplikSlice= createSlice({name:'kitaplik',initialState,reducers: {// Yeni kitap ekleme aksiyonu (Payload tipi kesin olarak 'Kitap' olmalı)kitapEkle: (state,action: PayloadAction<Kitap>) => {state.kitaplar.push(action.payload);},// Kitabın okuma durumunu tersine çevirme aksiyonu (Payload tipi 'string' id)durumDegistir: (state,action: PayloadAction<string>) => {constkitap= state.kitaplar.find(k => k.id === action.payload);if(kitap) {kitap.okundu = !kitap.okundu;}},// Kitap silme aksiyonukitapSil: (state,action: PayloadAction<string>) => {state.kitaplar = state.kitaplar.filter(k => k.id !== action.payload);}}});// Aksiyonları dışarı aktarıyoruzexportconst{kitapEkle,durumDegistir,kitapSil} = kitaplikSlice.actions;// ==========================================// 3. CENTRAL STORE (MERKEZİ DEPO) AYARI// ==========================================exportconststore= configureStore({reducer: {kitaplik: kitaplikSlice.reducer}});// TypeScript için Store üzerinden otomatik tip çıkarma (Type Inference)exporttypeRootState = ReturnType<typeofstore.getState>;exporttypeAppDispatch =typeofstore.dispatch;// ==========================================// 4. REACT BİLEŞENİ (COMPONENT & UI)// ==========================================functionKitaplikUygulamasi() {// Merkezi depodan verileri TypeScript güvenliği ile çekiyoruzconstkitaplar= useSelector((state: RootState) => state.kitaplik.kitaplar);constdispatch= useDispatch<AppDispatch>();// Form girdileri için yerel stateconst[baslik,setBaslik] = React.useState('');const[yazar,setYazar] = React.useState('');consthandleEkle= (e: React.FormEvent) => {e.preventDefault();if(!baslik.trim() || !yazar.trim())return;// Yeni kitap nesnemizi kurallara (Kitap tipine) uygun oluşturuyoruzconstyeniKitap: Kitap = {id: Date.now().toString(),baslik,yazar,okundu:false};// Store'a gönderiyoruz (Dispatch)dispatch(kitapEkle(yeniKitap));// Inputları temizlesetBaslik('');setYazar('');};return(<divstyle={{padding:'20px',fontFamily:'Arial, sans-serif',maxWidth:'500px',margin:'0 auto'}}><h2>📚 Küçük Kitaplığım (TS + Redux)</h2>{/* Kitap Ekleme Formu */}<formonSubmit={handleEkle} style={{marginBottom:'20px',display:'flex',flexDirection:'column',gap:'8px'}}><inputtype="text"placeholder="Kitap Adı"value={baslik}onChange={(e) => setBaslik(e.target.value)}style={{padding:'8px'}}/><inputtype="text"placeholder="Yazar Adı"value={yazar}onChange={(e) => setYazar(e.target.value)}style={{padding:'8px'}}/><buttontype="submit"style={{padding:'8px',backgroundColor:'#007bff',color:'white',border:'none',cursor:'pointer'}}>Kitaplığa Ekle</button></form>{/* Kitap Listesi */}<ulstyle={{listStyleType:'none',padding:0}}>{kitaplar.map((kitap) => (<likey={kitap.id} style={{padding:'10px',border:'1px solid #ddd',marginBottom:'8px',display:'flex',justifyContent:'space-between',alignItems:'center',backgroundColor: kitap.okundu ?'#e2f0d9':'#fff'}}><div><strongstyle={{textDecoration: kitap.okundu ?'line-through':'none'}}>{kitap.baslik}</strong><spanstyle={{fontSize:'0.9em',color:'#666'}}>-{kitap.yazar}</span></div><divstyle={{display:'flex',gap:'5px'}}><buttononClick={() => dispatch(durumDegistir(kitap.id))} style={{cursor:'pointer'}}>{kitap.okundu ?'Okunmadı Yap':'Okundu Yap'}</button><buttononClick={() => dispatch(kitapSil(kitap.id))} style={{backgroundColor:'#dc3545',color:'white',border:'none',cursor:'pointer'}}>Sil</button></div></li>))}</ul></div>);}// Uygulamayı Store Provider ile Sarmalayarak Dışarı AçıyoruzexportdefaultfunctionApp() {return(<Providerstore={store}><KitaplikUygulamasi/></Provider>);}
TypeScript projelerinde form
yapılarının tiplerini belirlemek (Form
Girdilerinin Şeması)
ve YUP ile girilen verilerin kurallara uyup uymadığını
denetlemek, çalışma zamanı (runtime) hatalarını sıfıra
indirir.
Aşağıda bir E-Ticaret Giriş (Login) Formunun Formik, YUP ve TypeScript entegrasyonuyla nasıl yazıldığını adım adım inceleyebilirsiniz.
LoginForm.tsx)importReactfrom'react';import{useFormik}from'formik';import*asYupfrom'yup';// 1. FORM VERİLERİNİN TYPESCRIPT TİPİNİ TANIMLIYORUZinterfaceLoginValues{eposta:string;sifre:string;beniHatirla:boolean;}// 2. YUP İLE DOĞRULAMA KURALLARINI (VALIDATION SCHEMA) BELİRLİYORUZ// Yup.object().shape yapısı, yukarıdaki LoginValues interface'i ile birebir uyuşmalıdır.constloginValidationSchema= Yup.object().shape({eposta: Yup.string().email('Lütfen geçerli bir e-posta adresi giriniz!').required('E-posta alanı zorunludur!'),sifre: Yup.string().min(6,'Şifreniz en az 6 karakterden oluşmalıdır!').max(20,'Şifreniz en fazla 20 karakter olabilir!').required('Şifre alanı zorunludur!'),beniHatirla: Yup.boolean()});exportfunctionLoginForm() {// 3. FORMIK KANCASINI (HOOK) BAĞLIYORUZ// useFormik fonksiyonuna <LoginValues> tip takısını vererek içindeki tüm alanların tip güvenliğini sağlıyoruz.constformik= useFormik<LoginValues>({initialValues: {eposta:'',sifre:'',beniHatirla:false,},validationSchema: loginValidationSchema,// YUP şemasını buraya bağlıyoruzonSubmit: (values) => {// Formda hiçbir hata yoksa bu fonksiyon tetiklenir.console.log('Sunucuya gönderilecek güvenli veriler:', values);// Burada dispatch(loginUser(values)) gibi işlemler yapılabilir.},});return(<divstyle={{maxWidth:'400px',margin:'30px auto',padding:'20px',border:'1px solid #ccc',borderRadius:'5px'}}><h3>Giriş Yap</h3><formonSubmit={formik.handleSubmit} style={{display:'flex',flexDirection:'column',gap:'15px'}}>{/* E-posta Alanı */}<divstyle={{display:'flex',flexDirection:'column'}}><labelhtmlFor="eposta">E-posta</label><inputid="eposta"name="eposta"type="email"onChange={formik.handleChange}onBlur={formik.handleBlur}// Kullanıcının inputtan çıkıp çıkmadığını izlervalue={formik.values.eposta}style={{padding:'8px',border: formik.touched.eposta && formik.errors.eposta ?'1px solid red':'1px solid #ccc'}}/>{/* Eğer kullanıcı inputa dokunmuşsa (touched) ve YUP'ta bir hata varsa mesajı göster */}{formik.touched.eposta && formik.errors.eposta ? (<spanstyle={{color:'red',fontSize:'0.85em',marginTop:'4px'}}>{formik.errors.eposta}</span>) :null}</div>{/* Şifre Alanı */}<divstyle={{display:'flex',flexDirection:'column'}}><labelhtmlFor="sifre">Şifre</label><inputid="sifre"name="sifre"type="password"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.sifre}style={{padding:'8px',border: formik.touched.sifre && formik.errors.sifre ?'1px solid red':'1px solid #ccc'}}/>{formik.touched.sifre && formik.errors.sifre ? (<spanstyle={{color:'red',fontSize:'0.85em',marginTop:'4px'}}>{formik.errors.sifre}</span>) :null}</div>{/* Beni Hatırla Çentatısı (Checkbox) */}<divstyle={{display:'flex',alignItems:'center',gap:'8px'}}><inputid="beniHatirla"name="beniHatirla"type="checkbox"onChange={formik.handleChange}checked={formik.values.beniHatirla}/><labelhtmlFor="beniHatirla">Beni Hatırla</label></div>{/* Gönder Butonu */}<buttontype="submit"style={{padding:'10px',backgroundColor:'#28a745',color:'white',border:'none',borderRadius:'4px',cursor:'pointer'}}>Giriş Yap</button></form></div>);}
Tip
Koruması: formik.values.eposta
yazarken editörünüz otomatik olarak bunu tamamlar. Yanlışlıkla
formik.values.email
yazarsanız TypeScript kodu derlemez ve sizi erkenden uyarır.
Kapsamlı Hata Yönetimi:
formik.touched
yapısı sayesinde, kullanıcı daha e-posta alanını doldurmadan
şifre alanına geçtiğinde "Bu alan zorunludur" gibi
sinir bozucu erken uyarıların çıkması engellenir. Uyarılar
sadece kullanıcı inputa odaklanıp çıktıktan sonra (Blur
aşaması) görünür.
========================================================================
Formik ve YUP ile kurduğumuz güvenli form yapısını Material
UI (MUI) bileşenleriyle birleştirmek, projenin arayüz
kalitesini kurumsal bir seviyeye taşır. Material UI bileşenleri,
Formik'in ürettiği hata mesajlarını ve odaklanma durumlarını
(touched,
errors)
doğrudan destekleyecek şekilde tasarlanmıştır.
Aşağıdaki örnekte, bir önceki
LoginForm
yapısını MUI'nin Box,
TextField,
Button,
FormControlLabel
ve Checkbox
bileşenlerini kullanarak tamamen modern ve estetik bir görünüme
kavuşturuyoruz.
MuiLoginForm.tsx)importReactfrom'react';import{useFormik}from'formik';import*asYupfrom'yup';// Material UI Bileşenlerini İçeri AktarıyoruzimportBoxfrom'@mui/material/Box';importTextFieldfrom'@mui/material/TextField';importButtonfrom'@mui/material/Button';importCheckboxfrom'@mui/material/Checkbox';importFormControlLabelfrom'@mui/material/FormControlLabel';importTypographyfrom'@mui/material/Typography';importContainerfrom'@mui/material/Container';importPaperfrom'@mui/material/Paper';// 1. TYPESCRIPT TIP TANIMLAMASIinterfaceLoginValues{eposta:string;sifre:string;beniHatirla:boolean;}// 2. YUP GEÇERLİLİK ŞEMASIconstloginValidationSchema= Yup.object().shape({eposta: Yup.string().email('Lütfen geçerli bir e-posta adresi giriniz!').required('E-posta alanı zorunludur!'),sifre: Yup.string().min(6,'Şifreniz en az 6 karakterden oluşmalıdır!').required('Şifre alanı zorunludur!'),beniHatirla: Yup.boolean()});exportfunctionMuiLoginForm() {// 3. FORMIK ENTEGRASYONUconstformik= useFormik<LoginValues>({initialValues: {eposta:'',sifre:'',beniHatirla:false,},validationSchema: loginValidationSchema,onSubmit: (values) => {console.log('Material UI Formundan gelen temiz veriler:', values);},});return(// Container: Formu ekranın ortasında, belirli bir genişlikte tutar<Containercomponent="main"maxWidth="xs"style={{marginTop:'50px'}}>{/* Paper: Forma gölgeli, şık bir kart görünümü kazandırır */}<Paperelevation={3} style={{padding:'30px',borderRadius:'8px'}}>{/* Typography: MUI'nin yazı ve başlık bileşenidir */}<Typographycomponent="h1"variant="h5"align="center"gutterBottom style={{fontWeight:'bold'}}>Hesabınıza Giriş Yapın</Typography>{/* Box: Form elementlerini sarmalayan ve 'sx' veya style propu ile kolayca şekillenen kapsayıcı */}<Boxcomponent="form"onSubmit={formik.handleSubmit} noValidate style={{marginTop:'20px'}}>{/*MUI TextField: Kendi içinde <label>, <input> ve hata mesajı alanını barındırır.error: true olursa input kırmızı kenarlıklı olur.helperText: Inputun altında gösterilecek kılavuz veya hata yazısıdır.*/}<TextFieldmargin="normal"requiredfullWidthid="eposta"label="E-posta Adresi"name="eposta"autoComplete="email"autoFocusvariant="outlined"value={formik.values.eposta}onChange={formik.handleChange}onBlur={formik.handleBlur}error={formik.touched.eposta && Boolean(formik.errors.eposta)}helperText={formik.touched.eposta && formik.errors.eposta}/><TextFieldmargin="normal"requiredfullWidthname="sifre"label="Şifre"type="password"id="sifre"autoComplete="current-password"variant="outlined"value={formik.values.sifre}onChange={formik.handleChange}onBlur={formik.handleBlur}error={formik.touched.sifre && Boolean(formik.errors.sifre)}helperText={formik.touched.sifre && formik.errors.sifre}/>{/* FormControlLabel: Checkbox gibi elemanları etiket yazısıyla düzgünce hizalar */}<FormControlLabelcontrol={<Checkboxid="beniHatirla"name="beniHatirla"color="primary"checked={formik.values.beniHatirla}onChange={formik.handleChange}/>}label="Beni Hatırla"style={{marginTop:'10px',display:'block'}}/>{/* Button: MUI'nin hazır animasyonlu ve renk varyantlı buton bileşeni */}<Buttontype="submit"fullWidthvariant="contained"color="primary"style={{marginTop:'20px',padding:'10px 0',fontSize:'16px'}}>Giriş Yap</Button></Box></Paper></Container>);}
Dahili
FormHelperText
Mantığı: Klasik HTML formlarında hata mesajları için ayrı
bir <span>
açıp CSS ile kızartmamız gerekiyordu. MUI TextField
bileşeninde ise error
propuna true
değeri geldiği an, helperText
içine yazdığınız yazı otomatik olarak kırmızı renge bürünür
ve form düzenini bozmadan inputun tam altında hizalanır.
Estetik ve
UX Gücü: Kullanıcı input alanına tıkladığında (Focus),
arayüz etiketi (Label) şık bir animasyonla yukarı doğru kayar
(Floating
Label). Bu, modern web standartlarında en çok
tercih edilen kullanıcı deneyimi (UX) yöntemlerinden biridir.
Responsive Düzen:
fullWidth
ve maxWidth="xs"
gibi tanımlamalar sayesinde, formunuz hiçbir ekstra CSS yazmanıza
gerek kalmadan hem masaüstü bilgisayarlarda hem de cep
telefonlarında ekrana tam uyumlu (mobil uyumlu) şekilde görünür.
************************************************************************************************
Gönderdiğiniz dosya içeriğini istekleriniz doğrultusunda satır satır inceledim. Gereksiz ve anlamsız ifadeleri (dakika, saniye, önizleme, ders yazıları vb.) tamamen temizledim. Müfredat yapısını önce tasarladım, ardından konuları hiç özet yapmadan, detaylı açıklamalar, örnek kodlar ve uygulamalar ile aşağıda hazırladım.
Müfredat, bir yazılımcının bilmesi gereken en temel konulardan başlayarak ileri seviyeye doğru şu şekilde tasarlanmıştır:
Giriş ve Çevre Kurulumu: Kod yazacağımız ortamı tanıma.
Değişkenler ve Veri Tipleri: Bilgisayarın hafızasında bilgi saklama.
Mantıksal Koşullar: Programın kararlar almasını sağlama.
Döngüler (Loops): Tekrarlayan işleri otomatik yapma.
Fonksiyonlar (Functions): Kodları paketleme ve tekrar kullanma.
Bu kurs bittiğinde, bilgisayarların nasıl düşündüğünü anlayacaksınız. Kendi kendinize çalışan akıllı programlar yapabileceksiniz. Karşılaştığınız zor bir problemi, küçük parçalara bölerek kodlarla çözmeyi öğreneceksiniz. Kısacası, sadece kod yazmayı değil, bir yazılımcı gibi mantıklı düşünmeyi de kazanacaksınız.
Yazılım yazmak için bilgisayarımıza özel bir program kurmalıyız. Kodlarımızı yazacağımız bu yere Editör (IDE) denir. Bilgisayarın bizim yazdığımız kodları anlaması için de bir çeviriciye (derleyici/yorumlayıcı) ihtiyacımız vardır.
Ekrana yazı yazdırmak için dünyadaki tüm yazılımcılar ilk önce "Merhaba Dünya" yazar.
print("Merhaba Dünya!")Görev: Ekrana kendi adınızı ve yaşınızı yazdıran bir program yapın.
print("Benim adım Ahmet.")print("Ben 10 yaşındayım.")print("Kod yazmayı çok seviyorum!")
Değişkenleri, içine eşya koyduğumuz oyuncak kutularına benzetebiliriz. Kutunun üzerine bir isim yazarız ve içine bir oyuncak koyarız. Bilgisayarda da sayıları, yazıları bu kutularda saklarız.
Yazı tipi
(String): Tırnak içinde yazılır. Örn: "Ayşe"
Tam Sayı
tipi (Integer): Normal sayılardır. Örn: 25
Ondalıklı
Sayı tipi (Float): Virgüllü (noktalı) sayılardır. Örn:
5.5
oyuncak_kutusu="Kırmızı Araba"yas=12boy=1.45
Görev: Bir süper kahramanın özelliklerini değişkenlerde saklayıp ekrana yazdırın.
kahraman_adi="Yıldırım Çocuk"gucu="Elektrik Fırlatmak"hizi=300ucabilir_mi=Trueprint("Kahramanın Adı:", kahraman_adi)print("Özel Gücü:", gucu)print("Hızı (km/s):", hizi)
Bilgisayarların karar vermesi gerekir. Bunu "Eğer ... ise şöyle yap, değilse böyle yap" diyerek sağlarız. Tıpkı "Eğer dışarıda yağmur yağıyorsa şemsiye al, yağmıyorsa güneş gözlüğü al" demek gibidir.
hava_yagmurlu=Trueifhava_yagmurlu ==True:print("Şemsiyeni yanına al!")else:print("Güneş gözlüğünü takabilirsin.")
Görev: Boyu 140 santimetreden kısa olanların hız trenine binmesini engelleyen bir sistem yazın.
boy_olcusu=135ifboy_olcusu >=140:print("Hız trenine binebilirsiniz! İyi eğlenceler. 🎢")else:print("Üzgünüm, boyunuz tren için biraz kısa. 🛑")
Bir işi defalarca arka arkaya yapmamız gerekirse döngüleri kullanırız. Örneğin öğretmeniniz size ceza olarak 100 defa "Ödevimi yapacağım" yazdırdıysa, bunu tek tek yazmak yerine döngüye söyleriz ve bilgisayar bir saniyede yapar.
# Ekrana 5 defa Merhaba yazarforsayiinrange(5):print("Merhaba!")
Görev: Roket fırlatılışı için 5'ten geriye doğru sayan ve sonunda "Fırlat!" diyen bir program yapın.
forsayiin[5,4,3,2,1]:print(sayi,"...")print("🚀 Roket Fırlatıldı! 🚀")
Fonksiyonlar, bir defa yapıp mutfağa koyduğumuz ekmek kızartma makinesi gibidir. Ne zaman ekmek kızartmak istesek makineyi çağırırız. Kodlarımızı da bir isim altında toplarız ve lazım olduğunda sadece o ismi çağırarak çalıştırırız.
defsabah_selami():print("Günaydın! Harika bir gün dilerim.")# Fonksiyonu çağırmasabah_selami()
Görev: Kenar uzunlukları verilen bir dikdörtgenin alanını otomatik hesaplayan bir fonksiyon yazın.
defalan_hesapla(kisa_kenar,uzun_kenar):sonuc= kisa_kenar * uzun_kenarprint("Bu bahçenin alanı:", sonuc,"metrekaredir.")# Sihirbazı farklı sayılarla kullanalımalan_hesapla(5,10)alan_hesapla(3,7)
İstekleriniz doğrultusunda müfredatın ikinci bölümünü oluşturan İleri Seviye Konuları için satır satır tasarıma ve detaylı incelemeye geçiyorum. Konuları hiç özetlemeden, her konu için hem Oyun Yapımı hem de Matematiksel Hesaplama alanından ayrı ayrı olmak üzere ikişer adet kod örneği ve uygulama hazırladım.
Bu bölümde, programlarımızın daha büyük verileri tutmasını, gerçek dünya nesnelerini taklit etmesini ve hatalara karşı dayanıklı olmasını sağlayan mimarileri işleyeceğiz:
Gelişmiş Veri Yapıları (Listeler ve Sözlükler): Birden fazla veriyi düzenli saklama.
Nesne Tabanlı Programlama (OOP - Object Oriented Programming): Kodları gerçek dünya nesneleri gibi tasarlama.
Hata Yönetimi (Try / Except): Programın çömesini engelleme ve hataları yakalama.
Dosya İşlemleri (File Handling): Verileri bilgisayara kalıcı olarak kaydetme ve okuma.
Tek bir değişken içinde sadece bir değer saklayabilirken, Listeler (List) sayesinde tek bir kutuda yan yana dizilmiş onlarca değer saklayabiliriz. Sözlükler (Dictionary) ise anahtar ve değer (Key-Value) mantığıyla çalışır; tıpkı gerçek bir sözlükte kelimenin kendisi (anahtar) ve karşılığında açıklaması (değer) olması gibidir.
Görev: Bir kahramanın çantasındaki eşyaları listede tutan, kahramanın gücünü ve canını ise sözlük içinde saklayan bir sistem yapın.
# List kullanarak çanta (envanter) yapımısirt_cantasi= ["Tahta Kılıç","Demir Kalkan","Can İksiri","Gümüş Anahtar"]# Dictionary kullanarak karakter özellikleri yapımıkahraman_ozellikleri= {"İsim":"Savaşçı Ejder","Seviye":5,"Can":100,"Altın":250}# Oyundaki bir an: Kahraman can iksiri kullanıyor ve altın kazanıyorprint(kahraman_ozellikleri["İsim"],"maceraya başlıyor...")print("Çantadaki ilk eşya:", sirt_cantasi[0])# Özellik güncellemekahraman_ozellikleri["Can"] =100# Canı fulledikkahraman_ozellikleri["Altın"] +=50# 50 altın kazandıprint("Güncel Altın Miktarı:", kahraman_ozellikleri["Altın"])
Görev: Bir sınıftaki öğrencilerin matematik sınav notlarını saklayan ve bu notların aritmetik ortalamasını bulan bir program yazın.
# List kullanarak sınav notlarını saklamamatematik_notlari= [85,90,78,60,100,92,45]# Notların toplamını bulmatoplam_not= sum(matematik_notlari)ogrenci_sayisi= len(matematik_notlari)# Ortalama hesaplamasinif_ortalamasi= toplam_not / ogrenci_sayisiprint("Sınıftaki Toplam Öğrenci Sayısı:", ogrenci_sayisi)print("Sınıfın Matematik Not Ortalaması:", round(sinif_ortalamasi,2))
Nesne Tabanlı Programlama, kod yazarken dünyadaki nesneleri bilgisayara tanıtma şeklimizdir. Bir Sınıf (Class), bir nesnenin fabrikadaki kalıbıdır. O kalıptan ürettiğimiz her bir parçaya ise Nesne (Object) denir. Sınıfların içinde nesnenin özellikleri (Attributes) ve yapabildiği işler (Metotlar/Fonksiyonlar) bulunur.
Görev: Oyundaki canavarları üretmek için bir kalıp (sınıf) tasarlayın. Bu kalıptan iki farklı canavar yaratıp birbirlerine saldırtın.
classCanavar:def__init__(self,ad,can,hasar_gucu):self.ad = adself.can = canself.hasar_gucu = hasar_gucudefsaldir(self,hedef_canavar):print(f"⚔️{self.ad},{hedef_canavar.ad}adlı canavara saldırdı!")hedef_canavar.can -= self.hasar_gucuprint(f"🛑{hedef_canavar.ad}canavarının kalan canı:{hedef_canavar.can}")# Canavar kalıbından iki adet nesne üretiyoruzork= Canavar("Vahşi Ork",80,15)zombi= Canavar("Zombi",50,10)# Ork, Zombi'ye saldırıyorork.saldir(zombi)
Görev: Bir dairenin alanını ve çevresini nesne yönelimli olarak hesaplayan bir daire sınıfı tasarlayın.
classDaire:def__init__(self,yaricap):self.yaricap = yaricapself.pi =3.14159defalan_hesapla(self):returnself.pi * (self.yaricap **2)defcevre_hesapla(self):return2* self.pi * self.yaricap# 5 cm yarıçapında bir daire nesnesi oluşturalımkucuk_daire= Daire(5)print("Dairenin Alanı:", kucuk_daire.alan_hesapla())print("Dairenin Çevresi:", kucuk_daire.cevre_hesapla())
Yazılan programlar bazen beklenmedik durumlarla karşılaşır. Örneğin kullanıcıdan sayı istersiniz ama kullanıcı harf yazar. Bu durumda program çöker ve kapanır. Try-Except blokları, programın çökmesini engelleyerek hatayı nazikçe yakalamamızı ve kullanıcıya düzgün bir uyarı vermemizi sağlar.
Görev: Kullanıcının seçmek istediği karakter numarasını alırken harf yazması durumunda oyunun çökmesini engelleyin.
karakterler= ["1- Savaşçı","2- Büyücü","3- Okçu"]print("Karakter Listesi:", karakterler)try:secim= int(input("Lütfen karakter numarasını girin (1-3): "))print(f"Harika!{karakterler[secim-1]}seçildi. Oyuna aktarılıyorsunuz...")exceptValueError:print("⚠️ HATA: Lütfen sadece sayısal bir değer girin! Harf girilemez.")exceptIndexError:print("⚠️ HATA: Sadece 1, 2 veya 3 numaralı karakteri seçebilirsiniz.")
Görev: Matematikte bir sayının sıfıra bölünmesi tanımsızdır. Kullanıcı bir sayıyı sıfıra bölmeye çalıştığında çökmeyen bir hesap makinesi yapın.
defbolme_islemi(sayi1,sayi2):try:sonuc= sayi1 / sayi2returnsonucexceptZeroDivisionError:return"⚠️ HATA: Bir sayı 0'a bölünemez! Matematiksel kurallara aykırı."# Test edelimprint("Doğru İşlem (10 / 2):", bolme_islemi(10,2))print("Hatalı İşlem (10 / 0):", bolme_islemi(10,0))
Bilgisayardaki programlar
kapatıldığında hafızalarındaki tüm değişkenler silinir.
Verilerin bilgisayar kapansa bile kaybolmamasını istiyorsak, onları
hard diske bir .txt
veya veri dosyası olarak kaydetmemiz gerekir. Python ile dosya
oluşturabilir, içine yazılar yazabilir ve daha sonra bu yazıları
okuyabiliriz.
Görev: Oyuncunun kazandığı en yüksek skoru bir dosyaya kaydeden ve oyun her açıldığında o skoru oradan okuyan bir sistem yazın.
# Skoru dosyaya kaydetme fonksiyonudefskoru_kaydet(skor):withopen("high_score.txt","w")asdosya:dosya.write(str(skor))print("En yüksek skor başarıyla kaydedildi!")# Skoru dosyadan okuma fonksiyonudefskoru_oku():try:withopen("high_score.txt","r")asdosya:icerik= dosya.read()returnint(icerik)exceptFileNotFoundError:# Eğer dosya henüz hiç oluşturulmadıysa ilk skor 0'dırreturn0# Oyun açılıyor: Eski skoru okueski_rekor= skoru_oku()print("Oyuna hoş geldiniz! Şu anki rekorunuz:", eski_rekor)# Oyuncu yeni bir rekor kırdı diyelimyeni_skor=1250ifyeni_skor > eski_rekor:print("🎉 TEBRİKLER! Yeni rekor kırdınız!")skoru_kaydet(yeni_skor)
Görev: Yapılan ağır
matematiksel hesaplamaların sonuçlarını tarihsel bir seyir
izlemesi amacıyla bir rapor dosyasına (.txt)
ekleyerek (append) kaydedin.
deffaktoriyel_hesapla_ve_kaydet(sayi):# Faktöriyel hesaplama mantığıfaktoriyel=1foriinrange(1, sayi +1):faktoriyel *= i# Sonucu dosyaya ekleme ( 'a' modu ile eskisini silmeden altına yazar)withopen("matematik_raporu.txt","a", encoding="utf-8")asrapor:rapor.write(f"{sayi}sayısının faktöriyeli ={faktoriyel}\n")print(f"{sayi}! işlemi hesaplandı ve rapora yazıldı.")# Farklı hesaplamalar yapıp rapora gönderelimfaktoriyel_hesapla_ve_kaydet(5)faktoriyel_hesapla_ve_kaydet(6)
İstediğiniz üç büyük çalışmayı da hiçbir konuyu atlamadan, tamamen sıfırdan ve en ince detayına kadar hazırladım. Tüm projeler ve test sorulanı, yukarıda öğrendiğimiz listeler, sözlükler, fonksiyonlar, hata yönetimi ve nesne tabanlı programlama konularını pekiştirecek şekilde tasarlanmıştır.
Bu oyun; fonksiyonları, sözlükleri (karakter özellikleri için), listeleri (envanter sistemi için) ve döngüleri aktif olarak kullanır.
importrandom# Oyunun ana verileri (Sözlük ve Listeler)kahraman= {"İsim":"","Can":100,"Altın":0,"Saldırı Gücü":15}envanter= ["Tahta Kalkan"]defoyun_baslangici():print("=== KARANLIK MAĞARA MACERASINA HOŞ GELDİNİZ ===")kahraman["İsim"] = input("Kahramanınızın adı ne olsun?: ")print(f"\n{kahraman['İsim']}, bir mağaranın girişinde gözlerini açıyorsun.")print(f"Çantanda şu an şunlar var:{envanter}")magara_girişi()defdurum_goster():print(f"\n---{kahraman['İsim']}Durum Raporu ---")print(f"Can:{kahraman['Can']}| Altın:{kahraman['Altın']}| Çanta:{envanter}")print("-----------------------------------")defmagara_girişi():durum_goster()print("\nÖnünde iki farklı tünel var:")print("1 - Meşalelerle aydınlatılmış sağ tünel.")print("2 - Karanlık ve yarasaların sesinin geldiği sol tünel.")secim= input("Hangi tünele girmek istersin? (1 veya 2): ")ifsecim =="1":aydinlik_tunel()elifsecim =="2":karanlik_tunel()else:print("Yanlış seçim yaptın, panikten mağara tavanı çöktü!")kahraman["Can"] -=20magara_girişi()defaydinlik_tunel():print("\nAydınlık tünelde yürürken yerde parıldayan bir kılıç buluyorsun!")print("Kılıcı çantana ekledin. Saldırı gücün arttı!")envanter.append("Keskin Kılıç")kahraman["Saldırı Gücü"] +=10print("\nTünelin sonu büyük bir kapıya çıkıyor. Kapıyı açıyorsun...")canavar_odasi()defkaranlik_tunel():print("\nKaranlıkta ilerlerken ayağın bir taşa takılıyor ve gizli bir odaya düşüyorsun!")print("Yerde eski bir hazine sandığı var!")secim= input("Sandığı açmak ister misin? (Evet / Hayır): ").lower()ifsecim =="evet":altin_miktari= random.randint(30,80)print(f"🎉 Harika! Sandıktan{altin_miktari}altın çıktı!")kahraman["Altın"] += altin_miktarielse:print("Tedbirli davrandın ve sandığa dokunmadın.")print("\nGizli geçit seni büyük bir odaya çıkarıyor...")canavar_odasi()defcanavar_odasi():durum_goster()print("\n🛑 EYVAH! Karşına Mağara Trolü çıktı!")trol_can=50whiletrol_can >0andkahraman["Can"] >0:print(f"\nTrolün Canı:{trol_can}| Senin Canı:{kahraman['Can']}")print("1 - Saldır!")print("2 - Kaçmayı Dene!")secim= input("Kararın ne?: ")ifsecim =="1":print(f"⚔️ Trolün üzerine atıldın ve{kahraman['Saldırı Gücü']}hasar verdin!")trol_can -= kahraman["Saldırı Gücü"]iftrol_can >0:trol_hasar= random.randint(10,25)if"Tahta Kalkan"inenvanter:trol_hasar -=5print(f"🛡️ Kalkanın sayesinde trolün darbesini hafiflettin!")print(f"💥 Trol sana vurdu ve{trol_hasar}canın gitti!")kahraman["Can"] -= trol_hasarelifsecim =="2":ifrandom.random() >0.5:print("🏃♂️ Muhteşem bir hızla trolden kaçmayı başardın ve mağaradan dışarı çıktın!")oyun_kazan()returnelse:print("🛑 Kaçmaya çalışırken trol seni yakaladı ve sırtına vurdu!")kahraman["Can"] -=20else:print("Kararsız kaldın, trol boş durmadı!")kahraman["Can"] -=15ifkahraman["Can"] <=0:oyun_kayip()else:print("\n🏆 Trolü yendin! Mağaranın çıkış kapısı açıldı!")kahraman["Altın"] +=100oyun_kazan()defoyun_kazan():print("\n=============================================")print(f"TEBRİKLER! Oyunu kazandın,{kahraman['İsim']}!")print(f"Toplam Toplanan Altın:{kahraman['Altın']}")print("=============================================")defoyun_kayip():print("\n=============================================")print("Maalesef canın tükendi... ÖLDÜNÜZ.")print("Görünüşe göre mağara sana acımadı. Tekrar dene!")print("=============================================")# Oyunu başlatalım (Test etmek için bu satırı çalıştırabilirsiniz)oyun_baslangici()
Bu proje; Try-Except yapısını, listeleri, gelişmiş matematiksel döngüleri ve veri işlemeyi içinde barındırır. Kullanıcının girdiği veri kümesi üzerinde karmaşık analizler yapar.
defveri_girisi_al():sayilar= []print("=== Gelişmiş İstatistik Merkezi ===")print("Hesaplama yapmak istediğiniz sayıları tek tek girin.")print("Veri girişini bitirmek için 'bitti' yazın.\n")whileTrue:girdi= input("Bir sayı girin: ").strip().lower()ifgirdi =="bitti":breaktry:sayi= float(girdi)sayilar.append(sayi)exceptValueError:print("⚠️ Hata: Lütfen geçerli bir sayı girin ya da 'bitti' yazın.")returnsayilardefortalama_hesapla(liste):iflen(liste) ==0:return0returnsum(liste) / len(liste)defmedyan_hesapla(liste):iflen(liste) ==0:return0sirali_liste= sorted(liste)n= len(sirali_liste)orta= n //2ifn %2==1:# Eleman sayısı tek ise ortadaki sayıreturnsirali_liste[orta]else:# Eleman sayısı çift ise ortadaki iki sayının ortalamasıreturn(sirali_liste[orta -1] + sirali_liste[orta]) /2defstandart_sapma_hesapla(liste,ortalama):iflen(liste) <=1:return0toplam_kare_fark=0forsayiininput_listesi:toplam_kare_fark += (sayi - ortalama) **2# Varyans ve ardından karekök (Standart Sapma)varyans= toplam_kare_fark / (len(liste) -1)returnvaryans **0.5defrapor_ekrani(liste):ifnotliste:print("🛑 Hiç veri girilmediği için hesaplama yapılamadı.")return# Hesaplamaları yapalımeleman_sayisi= len(liste)en_kucuk= min(liste)en_buyuk= max(liste)toplam= sum(liste)ort= ortalama_hesapla(liste)med= medyan_hesapla(liste)sapma= standart_sapma_hesapla(liste, ort)# Sonuçları ekrana yazdıralımprint("\n=====================================")print("📊 VERİ ANALİZİ RAPORU")print("=====================================")print(f"Girilen Sayılar :{liste}")print(f"Veri Adedi :{eleman_sayisi}")print(f"En Küçük Sayı :{en_kucuk}")print(f"En Büyük Sayı :{en_buyuk}")print(f"Sayıların Toplamı :{toplam}")print(f"Aritmetik Ortalama :{round(ort,2)}")print(f"Medyan (Orta Değer) :{round(med,2)}")print(f"Standart Sapma :{round(sapma,2)}")print("=====================================")# Programı çalıştıralıminput_listesi= veri_girisi_al()rapor_ekrani(input_listesi)
Aşağıdaki sorular, kurstaki ileri düzey mantıkları ne kadar iyi kavradığınızı ölçmek için hazırlanmıştır. Cevapları alt kısımda bulabilirsiniz.
cantam= ["Elma","Ekmek","Su"]karakter= {"Ad":"Sihirbaz","Envanter": cantam}cantam.append("Asa")print(karakter["Envanter"])
Yukarıdaki kod çalıştırıldığında ekranda ne görünür?
A) ['Elma',
'Ekmek', 'Su']
B) ['Elma',
'Ekmek', 'Su', 'Asa']
C) Hata mesajı verir.
Bir Araba
sınıfımız (Class) var ve bu sınıftan kirmizi_araba
ve mavi_araba
adında iki farklı araba ürettik.
Bu durumda
kirmizi_araba
ifadesi yazılım dilinde ne olarak adlandırılır?
A) Sınıf (Class)
B) Metot (Method)
C) Nesne / Örnek (Object / Instance)
try:print(10/0)exceptValueError:print("Geçersiz değer hatası!")exceptZeroDivisionError:print("Sıfıra bölme hatası!")
Yukarıdaki kod ekrana hangi mesajı yazar?
A) Geçersiz değer hatası!
B) Sıfıra bölme hatası!
C) Kod çöker ve hata ekranı gelir.
Python'da mevcut bir dosyanın
içindeki eski yazıları silmeden,
yeni verileri dosyanın en altına eklemek
istiyoruz.
Bunun için open()
fonksiyonunda hangi modu kullanmalıyız?
A) "w"
(Write)
B) "r"
(Read)
C) "a"
(Append)
Doğru Cevap: B
Açıklama:
Listeler referans tiplidir. cantam
listesine bir şey eklediğinizde, o listeyi kullanan karakter
sözlüğündeki veri de otomatik olarak güncellenir.
Doğru Cevap: C
Açıklama:
Araba
bir kalıptır (Sınıftır). O kalıptan üretilen her gerçek
araba ise birer Nesne (Object) durumundadır.
Doğru Cevap: B
Açıklama:
Bilgisayarda bir sayı sıfıra bölünmeye çalışıldığında
ZeroDivisionError
tetiklenir. Python bu hatayı yakalar ve onun altındaki bloku
çalıştırır.
Doğru Cevap: C
Açıklama: "w"
modu dosyayı sıfırlar ve baştan yazar. "a"
(Append) modu ise mevcut dosyanın sonuna ekleme yapmayı sağlar.
***************************************************
Bu dosyayı sizin için satır satır inceledim ve istediğiniz kurallara göre (dakika/saniye bilgileri, "Önizleme" ve "ders" satırları temizlenerek, anlamsız kısımlar elenerek) baştan sona detaylı bir şekilde hazırladım.
Aşağıda önce "Öğrenecekleriniz" bölümünün açıklaması, ardından tüm kurs konularının detaylı açıklamaları, örnekleri ve uygulama adımları yer almaktadır.
Bu bölüm, kursu bitirdiğinizde kazanacağınız becerileri ve hedefleri anlatır:
Sıfırdan İleri Seviyeye Ulaşmak: React dünyasına hiç bilmeseniz bile girip, modern bir geliştirici olarak çıkmanızı hedefler.
Büyük Projeler Yapmak: Sadece teorik değil, piyasada karşınıza çıkacak büyüklükte ve güzellikte 10'dan fazla proje üreteceksiniz.
İş Dünyasına Hazır Olmak: Şirketlerin projelerinde kullandığı popüler araçları ve kütüphaneleri (Redux, Tailwind, Supabase vb.) öğreneceksiniz.
Kıdemli Mühendis Gibi Düşünmek: Kod yazarken ileri seviye tasarım kalıpları (design patterns) kullanarak profesyonel çözümler üreteceksiniz.
Modern Teknolojilere Hakim Olmak: Next.js, Sunucu Bileşenleri (Server Components), veri yönetimi ve performans optimizasyonu gibi güncel konuları tamamen çözeceksiniz.
React, web sitelerinin kullanıcı arayüzlerini (UI) oluşturmak için kullanılan bir kütüphanedir. Eski yöntemlerde (Vanilla JavaScript) ekrandaki bir yazıyı değiştirmek için önce o elementi bulup sonra içini tek tek değiştirmemiz gerekirdi. React ise bunu otomatik ve çok daha hızlı yapar.
Örnek: Bir web sitesinde "Sepete Ekle" butonuna basıldığında yukarıdaki sepet sayısının anında artması React sayesinde çok kolaylaşır.
React yazabilmek için modern JavaScript kurallarını çok iyi bilmek gerekir.
Destructuring (Parçalara Ayırma): Bir nesnenin veya dizinin içindeki verileri kolayca dışarı çıkarma yöntemidir.
Örnek:
const
kullanıcı = { isim: "Ahmet", yas: 10 };
-> const
{ isim } = kullanıcı; artık elimizde
doğrudan isim
değişkeni var.
Arrow Functions (Ok Fonksiyonları): Fonksiyonları daha kısa yazma yoludur.
Örnek:
const
topla = (a, b) => a + b;
Array
Methods (Dizi Yöntemleri): map
(diziyi dönüştürür), filter
(belli şartı sağlayanları seçer), reduce
(diziyi tek bir değere indirir).
Uygulama:
Sayılardan oluşan bir listeyi map
ile ekrana tek tek yazdırmak.
React uygulamaları yapboz parçalarından oluşur. Bu parçaların her birine Bileşen (Component) denir.
JSX: JavaScript içine HTML benzeri kodlar yazmamızı sağlayan özel bir yapıdır.
Props: Bir bileşenden diğerine bilgi göndermek için kullanılan paketlerdir. Props verileri asla değiştirilemez (Immutability).
Lists &
Conditional Rendering: Verileri listelemek (map
ile) ve ekranda bir şeyin sadece belirli bir şartla görünmesini
sağlamak (örneğin kullanıcı giriş yaptıysa "Hoş geldin"
yazmak).
Uygulama - Profil Kartı: Bir kişinin fotoğrafını, ismini ve yeteneklerini gösteren bir kart bileşeni tasarlamak. Bu yetenek listesini dinamik olarak ekrana basmak.
Bileşenlerin kendi içlerinde
değişebilen bilgileri tutmasına State (Durum)
denir. useState
kancası (hook) ile oluşturulur.
Controlled Elements (Kontrollü Elemanlar): Form içindeki yazı kutularının (input) içindeki değerin React state'i tarafından kontrol edilmesidir.
Uygulama - Seyahat Listesi: Kullanıcının gideceği yerleri ve kaç eşya alacağını seçip listeye ekleyebildiği interaktif bir form uygulaması yapmak.
State'lerin nerede durması gerektiğine karar verme sürecidir. Eğer iki farklı çocuk bileşen aynı veriyi kullanacaksa, veri onların ortak babasına taşınır (Lifting State Up).
Derived State (Türetilmiş Durum): Var olan bir state'ten hesaplanabilen bilgiler için yeni bir state oluşturulmaz, doğrudan hesaplanır.
Örnek: Sepetteki ürünlerin toplam fiyatını bulmak için yeni bir state açmak yerine, ürünler listesinden anlık hesaplama yapmak.
Uygulama - Bahşiş Hesaplayıcı: Hesabı ve memnuniyet oranını seçerek ödenecek toplam tutarı ekranda dinamik gösteren bir araç yapmak.
Proje Amacı: Arkadaşlar listesi oluşturmak, yeni arkadaş eklemek, bir arkadaşı seçip onunla yenen yemeğin hesabını adilce bölüşmek. Tüm bu işlemler için dizilerde ekleme ve güncelleme gibi state operasyonları uygulanır.
Bileşenleri iç içe geçirerek
daha esnek yapılar kurma yöntemidir. children
prop'u kullanılarak bir bileşenin içine başka kodlar
gönderilebilir. Bu sayede "Prop Drilling" denilen, veriyi
en alttaki bileşene ulaştırmak için aradaki tüm bileşenlere
gereksizce taşıma derdinden kurtulunur.
Uygulama - Yıldız Derecelendirme: Tıklandığında veya üzerine gelindiğinde (hover) renk değiştiren, dışarıdan aldığı puan sınırına göre şekillenen dinamik bir yıldız bileşeni yapmak.
React'in ekranı nasıl güncellediğini anlamak çok önemlidir.
Virtual DOM (Sanal DOM) & Diffing: React ekrandaki her şeyi hafızada simüle eder. Bir şey değiştiğinde eski haliyle yeni halini karşılaştırır (Diffing) ve sadece değişen küçük parçayı gerçek ekranda günceller.
Key Prop: Listeler oluşturulurken React'in elemanları karıştırmaması için her elemana verilen özel ve benzersiz bir kimliktir.
React bileşenlerinin dış dünya
ile iletişim kurmasını (örneğin internetten veri indirmek)
sağlayan yapı useEffect
hook'udur.
Bağımlılık Dizisi (Dependency Array): Bu efektin ne zaman çalışacağını söyler. Boş bırakılırsa sayfa ilk açıldığında çalışır.
Cleanup Function (Temizleme Fonksiyonu): Efekt biterken veya bileşen ekrandan kaybolurken arkada açık kalan istekleri iptal etmek için kullanılır.
Uygulama - usePopcorn Projesi: İnternetteki bir film sitesinden (API) film verilerini çekmek, ekranda yükleniyor (loading) animasyonu göstermek, hataları yakalamak ve izlenen filmleri tarayıcı hafızasına kaydetmek.
useRef: Ekranda yeniden çizime (render) sebep olmadan bilgi saklamak veya doğrudan bir HTML elementine (örneğin arama kutusuna otomatik odaklanmak için) erişmek için kullanılır.
Custom Hooks: Kendimize ait tekrar kullanılabilir React kancaları üretmektir.
Uygulama: Sayfa
konumunu bulan useGeolocate
veya tarayıcı hafızasını yöneten useLocalStorageState
kancalarını sıfırdan yazmak.
Eğer bir sayfada birbirine bağlı
çok fazla state varsa, useState
yerine useReducer
kullanılır. Tüm durum değişiklikleri "Action" adı
verilen emirlerle tek bir merkezden (Reducer fonksiyonu) yönetilir.
Uygulama - React Quiz: Süreli, puanlı, çoktan seçmeli sorulardan oluşan gelişmiş bir bilgi yarışması uygulaması yapmak.
Sayfa yenilenmeden, linklere tıklandığında anında başka sayfalara geçiş yapılmasını sağlar. URL üzerinden parametre okuma ve yönlendirme işlemleri yapılır.
Uygulama - WorldWise: Gezilen şehirleri ve ülkeleri harita üzerinde işaretleyip kaydedebileceğimiz harita destekli (Leaflet kütüphanesi ile) büyük bir seyahat günlüğü uygulaması.
Uygulamanın en tepesinden en
altındaki bileşene, aradaki kimseye uğramadan veri gönderebilen
küresel bir depo sistemidir. Genelde useReducer
ile birleştirilerek devasa sistemler kurulur.
Örnek: Uygulamaya "Karanlık Mod / Aydınlık Mod" veya "Kullanıcı Giriş Bilgisi" eklemek.
Gereksiz ekran çizimlerini (Wasted Renders) engellemek için kullanılan yöntemlerdir.
memo,
useMemo
ve useCallback
araçları ile fonksiyonları ve ağır hesaplamaları hafızada
tutarak performansı artırırız.
Code Splitting (Kod Bölme): Sitenin tamamını tek seferde yüklemek yerine, kullanıcı hangi sayfaya girerse sadece o sayfanın kodlarını indirmesini sağlamaktır.
Dünyadaki büyük şirketlerin tercih ettiği en popüler küresel state yönetim kütüphanesidir. "Thunks" yapısı ile sunucu istekleri (API) Redux içinde yönetilir.
Uygulama - Banka Hesabı: Para yatırma, çekme, kredi alma işlemlerini Redux Toolkit kullanarak merkezi bir depoda güvenle simüle etmek.
Yeni nesil React Router sürümünde veriler sayfa açılmadan önce "Loader" fonksiyonları ile indirilir ve "Action" fonksiyonları ile form verileri sunucuya yazılır.
Uygulama - Fast React Pizza Co.: Menüden pizzaları seçip sepete eklediğimiz, adres ve konum bilgisi girerek sipariş verdiğimiz profesyonel bir pizza sipariş uygulaması.
Modern web sitelerini çok hızlı şekillendirmek için kullanılan, hazır sınıflardan (class) oluşan popüler bir CSS yapısıdır. Esnek kutular (Flexbox), ızgaralar (Grid) ve responsive (mobil uyumlu) tasarımlar bu bölümde projeye uygulanır.
Styled Components: JavaScript kodlarının içinde doğrudan CSS yazmamızı sağlayan bir yöntemdir.
Proje Planlaması - The Wild Oasis: Bir otel/otel odası yönetim paneli uygulaması. Bu panelde odalar, rezervasyonlar ve kullanıcılar yönetilecek.
Supabase, hazır bir veri tabanı ve kullanıcı yönetim sistemi sunan bir servistir. Bu bölümde tablolar oluşturulur, tablolar arası ilişkiler kurulur ve dosya yükleme (resimler için) alanları ayarlanır.
İnternetten gelen verileri hafızada tutmak (caching), eskiyen verileri arka planda otomatik yenilemek ve yüklenme/hata durumlarını çok kolay yönetmek için kullanılan harika bir kütüphanedir.
Uygulama: Otel odalarını listeleme, silme, yeni oda eklerken fotoğraf yükleme ve form hatalarını kontrol etme işlemlerini entegre etmek.
Bileşenleri üst seviye esnek yapmak için kullanılan profesyonel yazılım mimarileridir.
Compound Component Pattern: Birbiriyle ilişkili bileşenleri tek bir çatı altında toplama (Örn: Bir Modal açılır penceresi yapmak).
Render Props & Higher-Order Components: Kodu çoğaltmadan aynı mantığı farklı bileşenlerde kullanma yöntemleri.
Gelişmiş Özellikler: Grafik çizimleri (Recharts kütüphanesi), kullanıcı giriş/çıkış (Authentication), veri tabanı güvenlik kuralları (RLS) ve sayfalama (Pagination) işlemleri.
Canlıya Alma: Yapılan projeleri tüm dünyanın erişebileceği Netlify ve Vercel platformlarına yüklemek.
Next.js, React'in üzerine kurulu, sitelerin Google'da üst sıralarda çıkmasını sağlayan (SEO dostu) güçlü bir framework'tür.
Server-Side Rendering (SSR): Sayfaların kullanıcının bilgisayarında değil, sunucuda hazırlanıp hazır HTML olarak gönderilmesi mimarisidir.
React Server Components (RSC): Bileşenlerin doğrudan sunucuda çalışarak tarayıcıya hiç JavaScript yükü bindirmemesi teknolojisidir.
Otel yönetim panelinin yanı sıra, müşterilerin odaları görebileceği ve rezervasyon yapabileceği harici, çok hızlı bir web sitesi tasarımı. Font ve görsel optimizasyonları Next.js'in özel araçlarıyla yapılır.
React Suspense: Sayfanın ağır yüklenen kısımlarını beklerken sadece o bölgede bir yükleniyor animasyonu gösterip, kalan kısımları anında ekrana getirme yöntemidir.
Incremental Static Regeneration (ISR): Sayfaları statik tutup belli aralıklarla arka planda güncelleyerek mükemmel hız elde etme tekniğidir.
NextAuth (Auth.js): Google ile giriş yap gibi modern kimlik doğrulama sistemlerini güvenli şekilde kurmak.
Server Actions: Form gönderildiğinde hiçbir API rotası yazmadan, doğrudan sunucu tarafındaki bir fonksiyonu tetikleyerek veri tabanını güncelleme kolaylığıdır.
Modern Hooks: useFormStatus
(form yükleniyor durumu), useTransition
(arka plan geçişleri) ve useOptimistic
(veri tabanından onay gelmeden ekranda değişikliği anında
gösterip kullanıcıyı bekletmeme kancası).
Next.js projesinin yaratıcısı olan Vercel platformunda projeyi yayınlamak, çevre değişkenlerini (şifreler vb.) ayarlamak. Son olarak Next.js'in eski sürümü olan "Pages Router" mekanizmasını da genel hatlarıyla öğrenerek eski projeleri anlayabilecek seviyeye gelmek.
=======================================================================================================
Harika bir karar! Bu eğitim yol haritasındaki her konuyu ve uygulamayı, adım adım çalışan gerçek ve pratik kod örnekleriyle bir yazılım rehberine dönüştürüyoruz.
Tüm müfredatı tam anlamıyla pratiğe dökmek çok uzun ve kapsamlı bir süreçtir. Bu yüzden hiçbir detayı atlamadan, parça parça ve adım adım ilerleyeceğiz. Eğer ben kodları yazarken bir yerde durursam, kaldığım yeri bana hatırlatarak "devam et" demeniz yeterlidir.
İlk adımımız olan 1. Bölüm: React Temelleri ile hemen kodlamaya başlıyoruz!
React yazarken en çok kullanacağımız modern JavaScript (ES6+) özelliklerinin kod yapıları şunlardır:
// 1. Nesne Parçalara Ayırma (Object Destructuring)constkullanıcı= {isim:"Ahmet",yas:26,sehir:"Isparta"};const{isim,yas} = kullanıcı;console.log(isim);// Çıktı: Ahmet// 2. Ok Fonksiyonları (Arrow Functions)consttopla= (a,b) => a + b;console.log(topla(5,10));// Çıktı: 15// 3. Kısa Devre Değerlendirmesi (Short-Circuiting)constinputKullaniciAdi="";constgecerliIsim= inputKullaniciAdi ||"Misafir Kullanıcı";console.log(gecerliIsim);// Çıktı: Misafir Kullanıcı// 4. Diziyi Dönüştürme (Array Map)constsayilar=;constkaresi= sayilar.map(sayi => sayi * sayi);console.log(karesi);// Çıktı: [1, 4, 9]
React uygulamaları fonksiyonel bileşenlerden (Components) oluşur. Büyük harfle başlarlar ve geriye HTML benzeri bir JSX kodu döndürürler.
Aşağıdaki örnekte bir
ProfilKartı
bileşeni oluşturuyoruz ve ana bileşenden ona props
yardımıyla bilgi gönderiyoruz.
import React from "react";// 1. Alt Bileşen (Child Component) - Props parametresini alırfunction ProfilKartı(props) {return (<div style={{ border: "1px solid #ccc", padding: "20px", borderRadius: "8px", width: "250px" }}><img src={props.fotoUrl} alt={props.isim} style={{ width: "100%", borderRadius: "50%" }} /><h2>{props.isim}</h2><p>{props.meslek}</p><div className="yetenekler">{/* Diziyi map metodu ile JSX içinde listeliyoruz */}{props.yetenekler.map((yetenek, indeks) => (<span key={indeks} style={{ background: "#eee", padding: "5px", margin: "2px", display: "inline-block" }}>{yetenek}</span>))}</div></div>);}// 2. Ana Bileşen (Parent Component)export default function App() {const yetenekListesi = ["React", "JavaScript", "Tailwind CSS"];return (<div className="app"><h1>Yazılımcı Kadrosu</h1>{/* Alt bileşene özellikleri (props) gönderiyoruz */}<ProfilKartıisim: "Ahmet Yılmaz"meslek: "Ön Yüz Geliştirici"fotoUrl: "https://placeholder.com"yetenekler={yetenekListesi}/></div>);}
Bileşen içindeki verilerin
değişmesi ve ekranın güncellenmesi için useState
kancasını (hook) kullanırız.
Kullanıcının bir butona basarak
adımları ilerlettiği ve input alanına yazdığı veriyi kontrol
ettiği (Controlled
Element)
temel yapı:
import React, { useState } from "react";export default function AdimSayici() {// 1. State Tanımlamalarıconst [adim, setAdim] = useState(1);const [isim, setIsim] = useState("");// 2. Olay Yöneticileri (Event Handlers)function sonrakiAdim() {if (adim < 3) setAdim((mevcutAdim) => mevcutAdim + 1);}function oncekiAdim() {if (adim > 1) setAdim((mevcutAdim) => mevcutAdim - 1);}return (<div style={{ background: "#f7f7f7", padding: "20px", margin: "20px" }}>{/* Koşullu Stil ve Dinamik Sınıf Tasarımı */}<div style={{ display: "flex", justifyContent: "space-between" }}><span style={{ color: adim >= 1 ? "blue" : "black" }}>1. Giriş</span><span style={{ color: adim >= 2 ? "blue" : "black" }}>2. Bilgi Girişi</span><span style={{ color: adim === 3 ? "blue" : "black" }}>3. Onay</span></div><p style={{ textAlign: "center", margin: "20px 0" }}>Aktif Adım: {adim}</p>{/* Kontrollü Input Elemanı Örneği */}{adim === 2 && (<div style={{ margin: "10px 0" }}><label>Adınız: </label><inputtype="text"value={isim}onChange={(e) => setIsim(e.target.value)}/><p>Yazılan: {isim}</p></div>)}<div style={{ display: "flex", justifyContent: "center", gap: "10px" }}><button onClick={oncekiAdim}>Geri</button><button onClick={sonrakiAdim}>İleri</button></div></div>);}
Eğer bir formdan eklenen veri, başka bir bileşendeki listede gösterilecekse state'i ikisinin ortak babasına taşırız.
Bu uygulama; form girişi, listeleme, eleman silme ve istatistik hesaplama (Türetilmiş Durum - Derived State) mantığını tam olarak gösterir.
import React, { useState } from "react";// 1. ANA BİLEŞENexport default function SeyahatUygulamasi() {// State babada duruyor, böylece hem forma hem listeye dağıtabiliyoruzconst [esyalari, setEsyalari] = useState([]);// Yeni eşya ekleme fonksiyonu (Child-to-Parent iletişimi için aşağıya gönderilecek)function handleEsyaEkle(yeniEsya) {setEsyalari((eskiEsyalar) => [...eskiEsyalar, yeniEsya]);}// Eşya silme fonksiyonufunction handleEsyaSil(id) {setEsyalari((eskiEsyalar) => eskiEsyalar.filter((esya) => esya.id !== id));}// Türetilmiş Durum (Derived State) - Yeni bir state açmaya gerek yok!const toplamEsya = esyalari.length;return (<div style={{ fontFamily: "sans-serif", padding: "20px" }}><h1>Seyahat Bagaj Listem 🧳</h1><Form_Bileseni onEsyaEkle={handleEsyaEkle} /><Liste_Bileseni esyalar={esyalari} onEsyaSil={handleEsyaSil} /><Istatistik_Bileseni toplam={toplamEsya} /></div>);}// 2. FORM BİLEŞENİfunction Form_Bileseni({ onEsyaEkle }) {const [tanim, setTanim] = useState("");function handleSubmit(e) {e.preventDefault();if (!tanim) return;const yeniEsya = { id: Date.now(), tanim, alindi: false };onEsyaEkle(yeniEsya); // Babaya veriyi gönderiyoruzsetTanim(""); // Input'u temizle}return (<form onSubmit={handleSubmit} style={{ margin: "20px 0" }}><inputtype="text"placeholder="Eşya adı..."value={tanim}onChange={(e) => setTanim(e.target.value)}/><button type="submit">Ekle</button></form>);}// 3. LİSTE BİLEŞENİfunction Liste_Bileseni({ esyalar, onEsyaSil }) {return (<ul>{esyalar.map((esya) => (<li key={esya.id} style={{ margin: "5px 0" }}>{esya.tanim}<button onClick={() => onEsyaSil(esya.id)} style={{ marginLeft: "10px", color: "red" }}>Sil</button></li>))}</ul>);}// 4. İSTATİSTİK BİLEŞENİfunction Istatistik_Bileseni({ toplam }) {return (<footer style={{ marginTop: "20px", fontWeight: "bold" }}>{toplam === 0 ? "Listeniz boş, hazırlığa başlayın!" : `Toplam ${toplam} parça eşya listelendi.`}</footer>);}
Bu proje, yukarıda öğrendiğimiz tüm temelleri (State güncelleme, seçili eleman yönetimi, matematiksel hesaplar) bir araya getirir.
import React, { useState } from "react";const ilkArkadaslar = [{ id: 1, isim: "Ali", bakiye: -10 }, // Ali'ye 10 TL borcumuz var{ id: 2, isim: "Merve", bakiye: 20 }, // Merve'nin bize 20 TL borcu var];export default function EatNSplit() {const [arkadaslar, setArkadaslar] = useState(ilkArkadaslar);const [seciliArkadas, setSeciliArkadas] = useState(null);function handleArkadasSec(arkadas) {// Eğer zaten seçiliyse kapat, değilse seçsetSeciliArkadas((mevcut) => (mevcut?.id === arkadas.id ? null : arkadas));}function handleHesapBolus(deger) {setArkadaslar((eskiArkadaslar) =>eskiArkadaslar.map((arkadas) =>arkadas.id === seciliArkadas.id? { ...arkadas, bakiye: arkadas.bakiye + deger }: arkadas));setSeciliArkadas(null); // Formu kapat}return (<div style={{ display: "flex", gap: "40px", padding: "20px" }}><div style={{ width: "300px" }}><h2>Arkadaşlar</h2>{arkadaslar.map((arkadas) => (<div key={arkadas.id} style={{ display: "flex", justifyContent: "space-between", margin: "10px 0" }}><span>{arkadas.isim}{arkadas.bakiye < 0 && <p style={{ color: "red" }}>{arkadas.isim}'ye {Math.abs(arkadas.bakiye)} TL borcun var.</p>}{arkadas.bakiye > 0 && <p style={{ color: "green" }}>{arkadas.isim}'nin sana {arkadas.bakiye} TL borcu var.</p>}{arkadas.bakiye === 0 && <p>Ödeştiniz.</p>}</span><button onClick={() => handleArkadasSec(arkadas)}>{seciliArkadas?.id === arkadas.id ? "Kapat" : "Seç"}</button></div>))}</div>{seciliArkadas && (<FormHesapBolus arkadas={seciliArkadas} onHesapBolus={handleHesapBolus} />)}</div>);}function FormHesapBolus({ arkadas, onHesapBolus }) {const [toplamHesap, setToplamHesap] = useState("");const [benimGiderim, setBenimGiderim] = useState("");const arkadasinGideri = toplamHesap ? toplamHesap - benimGiderim : "";const [kimOduyor, setKimOduyor] = useState("ben");function handleSubmit(e) {e.preventDefault();if (!toplamHesap || !benimGiderim) return;// Hesaplama mantığı: Kimin kime borçlandığını bulmaconst bakiyeDegisimi = kimOduyor === "ben" ? arkadasinGideri : -benimGiderim;onHesapBolus(bakiyeDegisimi);}return (<form onSubmit={handleSubmit} style={{ background: "#eee", padding: "20px", borderRadius: "8px" }}><h3>{arkadas.isim} ile Hesabı Bölüş</h3><div><label>💰 Toplam Tutar: </label><input type="number" value={toplamHesap} onChange={(e) => setToplamHesap(Number(e.target.value))} /></div><div><label>🕴️ Senin Giderin: </label><input type="number" value={benimGiderim} onChange={(e) => setBenimGiderim(Number(e.target.value))} /></div><div><label>👥 {arkadas.isim}'nin Gideri: </label><input type="text" disabled value={arkadasinGideri} /></div><div><label>💳 Hesabı Kim Ödedi? </label><select value={kimOduyor} onChange={(e) => setKimOduyor(e.target.value)}><option value="ben">Ben</option><option value="arkadas">{arkadas.isim}</option></select></div><button type="submit" style={{ marginTop: "10px" }}>Hesabı Bölüştür</button></form>);}
Harika! Aldığım onay ile birlikte hiç vakit kaybetmeden 2. BÖLÜM: ORTA SEVİYE REACT dünyasına adım atıyoruz. Bu bölümde bileşenlerin dış dünya ile nasıl konuştuğunu, internetten canlı veri çekmeyi, tarayıcı hafızasını kontrol etmeyi ve kendi kancalarımızı (Custom Hooks) sıfırdan yazmayı kodlayarak öğreneceğiz.
children
Bileşenleri esnek hale getirmek ve
"Prop Drilling" (veriyi kat kat aşağı taşıma) sorununu
çözmek için children
prop'unu kullanırız. Bu yapı, bir bileşenin içine HTML veya
başka bileşenler yerleştirmemizi sağlar.
Kullanıcının üzerine gelip (hover) tıklayabildiği, dışarıdan aldığı özelliklere göre şekillenen profesyonel bir yıldız bileşeni yapısı:
import React, { useState } from "react";// 1. TEK BİR YILDIZ BİLEŞENİfunction Yildiz({ dolu, onSec, onMauseGirdi, onMauseCikti }) {const yildizStili = {width: "24px",height: "24px",cursor: "pointer",display: "inline-block",color: "#ffa31a",fontSize: "24px"};return (<spanstyle={yildizStili}onClick={onSec}onMouseEnter={onMauseGirdi}onMouseLeave={onMauseCikti}>{dolu ? "★" : "☆"}</span>);}// 2. ANA YILDIZ DERECE BİLEŞENİ (Yeniden Kullanılabilir API)export function YildizDerecelendirme({ maksimumYildiz = 5, varsayilanPuan = 0, onPuanVer }) {const [puan, setPuan] = useState(varsayilanPuan);const [geciciPuan, setGeciciPuan] = useState(0);function handlePuanla(yeniPuan) {setPuan(yeniPuan);if (onPuanVer) onPuanVer(yeniPuan); // Dış dünyaya puanı bildiriyoruz}return (<div style={{ display: "flex", alignItems: "center", gap: "10px" }}><div>{Array.from({ length: maksimumYildiz }, (_, i) => {const aktifIndeks = i + 1;return (<Yildizkey={i}dolu={geciciPuan ? geciciPuan >= aktifIndeks : puan >= aktifIndeks}onSec={() => handlePuanla(aktifIndeks)}onMauseGirdi={() => setGeciciPuan(aktifIndeks)}onMauseCikti={() => setGeciciPuan(0)}/>);})}</div><p style={{ margin: 0, fontWeight: "bold" }}>{geciciPuan || puan || ""} Puan</p></div>);}
useEffect
& Data Fetching)
Bileşen ilk açıldığında veya
bir arama kelimesi değiştiğinde harici bir API'den (sunucudan)
veri çekmek için useEffect
kullanırız.
Bu kod yapısında ücretsiz bir API kullanıyormuşuz gibi simülasyon yaparak; Yükleniyor (Loading), Hata Yönetimi (Error Handling) ve Arama Sıfırlama mantığını kuruyoruz.
import React, { useState, useEffect } from "react";export default function FilmUygulamasi() {const [aramaKelimesi, setAramaKelimesi] = useState("Batman");const [filmler, setFilmler] = useState([]);const [yukleniyor, setYukleniyor] = useState(false);const [hataMetni, setHataMetni] = useState("");useEffect(() => {// 1. Ağ isteklerini iptal etmek için AbortController (Cleanup mekanizması)const kontrolcu = new AbortController();async function filmVerisiCek() {try {setYukleniyor(true);setHataMetni(""); // Hataları sıfırla// Sahte API isteği simülasyonu (Gerçek projede fetch kullanılır)const url = `https://omdbapi.com{aramaKelimesi}`;// Örnek amaçlı gecikme ve veri simülasyonu yapıyoruz:if (aramaKelimesi.length < 3) {setFilmler([]);return;}// Örnek veri seticonst sahteSonuc = [{ imdbID: "1", Title: `${aramaKelimesi} Başlıyor`, Year: "2020", Type: "movie" },{ imdbID: "2", Title: `${aramaKelimesi} Dönüyor`, Year: "2023", Type: "movie" },];setFilmler(sahteSonuc);} catch (hata) {if (hata.name !== "AbortError") {setHataMetni("Filmler yüklenirken bir sorun oluştu!");}} finally {setYukleniyor(false);}}filmVerisiCek();// 2. Temizleme Fonksiyonu (Cleanup Function)// Kullanıcı çok hızlı yazı yazarken eski yarım kalan istekleri iptal eder.return () => {kontrolcu.abort();};}, [aramaKelimesi]); // Arama kelimesi her değiştiğinde bu efekt baştan çalışır.return (<div style={{ padding: "20px", fontFamily: "sans-serif" }}><h2>🍿 Popcorn Film Dünyası</h2><inputtype="text"placeholder="Film ara..."value={aramaKelimesi}onChange={(e) => setAramaKelimesi(e.target.value)}style={{ padding: "8px", width: "250px", marginBottom: "20px" }}/>{/* Durumsal Ekran Gösterimleri */}{yukleniyor && <p>🎬 Filmler yükleniyor, lütfen bekleyin...</p>}{hataMetni && <p style={{ color: "red" }}>❌ {hataMetni}</p>}{!yukleniyor && !hataMetni && (<ul>{filmler.map((film) => (<li key={film.imdbID} style={{ margin: "10px 0" }}><strong>{film.Title}</strong> ({film.Year})</li>))}</ul>)}</div>);}
useRef
ile DOM Elementlerine Erişmek ve Veri Saklamak
useRef,
ekranda güncellemeye (render) sebep olmadan arkada bilgi saklamak ya
da bir HTML etiketine (girdi kutusu gibi) doğrudan odaklanmak
(focus) için kullanılır.
import React, { useEffect, useRef } from "react";export default function OdaklanmaBileseni() {const inputElementiRef = useRef(null);const tiklamaSayisiRef = useRef(0); // Ekranda görünmeyen ama arkada artan sayaçuseEffect(() => {// Sayfa ilk açıldığında girdi kutusuna otomatik odaklan (Mavi çerçeve yap)inputElementiRef.current.focus();}, []);function handleButonClick() {tiklamaSayisiRef.current = tiklamaSayisiRef.current + 1;console.log("Butona arka planda tıklanma sayısı: ", tiklamaSayisiRef.current);// Dikkat: Bu buton tıklandığında ekrandaki sayaç güncellenmez, çünkü useRef render tetiklemez!}return (<div style={{ padding: "20px" }}><input ref={inputElementiRef} type="text" placeholder="Bana otomatik odaklanıldı..." /><button onClick={handleButonClick} style={{ marginLeft: "10px" }}>Arkada Sayaç Artır</button></div>);}
Farklı bileşenlerde tekrar tekrar
kullandığımız useEffect
veya useState
mantıklarını bir araya toplayıp kendi fonksiyonumuzu üretmeye
Custom Hook denir.
İsmi mutlaka use
ile başlamalıdır.
useLocalStorageState
(Verileri Otomatik Hafızada Tutma Kancası)
Bu kanca, verdiğimiz state
bilgisini tarayıcının yerel hafızasına (localStorage)
otomatik kaydeder ve sayfa yenilense bile veriyi geri getirir.
import{useState,useEffect}from"react";// ÖZEL KANCA TANIMIexportfunctionuseLocalStorageState(baslangicDegeri,anahtarKelime) {// Lazy Initialization: İlk değeri hafızadan okuyarak başlatıyoruzconst[deger,setDeger] = useState(() => {constkaydedilenVeri= localStorage.getItem(anahtarKelime);returnkaydedilenVeri ? JSON.parse(kaydedilenVeri) : baslangicDegeri;});// Değer her değiştiğinde local storage alanını güncelleuseEffect(() => {localStorage.setItem(anahtarKelime, JSON.stringify(deger));}, [deger, anahtarKelime]);return[deger, setDeger];}
Yukarıda yazdığımız kancayı
normal bir useState
gibi projemizde şu şekilde kullanırız:
import React from "react";import { useLocalStorageState } from "./useLocalStorageState"; // Kancayı çağırıyoruzexport function NotDefteriUygulamasi() {// State'imiz artık otomatik olarak tarayıcı hafızasına bağlı!const [notlar, setNotlar] = useLocalStorageState([], "kullanici_notlari");const [yeniNot, setYeniNot] = React.useState("");function notEkle() {if (!yeniNot) return;setNotlar([...notlar, yeniNot]);setYeniNot("");}return (<div style={{ padding: "20px" }}><h3>📝 Yerel Hafızalı Not Defteri</h3><input value={yeniNot} onChange={(e) => setYeniNot(e.target.value)} /><button onClick={notEkle}>Notu Hafızaya Ekle</button><ul>{notlar.map((not, i) => <li key={i}>{not}</li>)}</ul><button onClick={() => setNotlar([])} style={{ color: "red" }}>Hafızayı Temizle</button></div>);}
React'e kancalar (hooks) gelmeden önce (2019 öncesi), state ve yaşam döngüsü metotları sınıf bileşenleri ile yazılırdı. Müfredatta yer alan bu yapının temel mantığı şudur:
import React, { Component } from "react";export default class KlasikHavaDurumu extends Component {// 1. Sınıf içinde state tanımlamaconstructor(props) {super(props);this.state = {sehir: "Isparta",derece: 24};}// 2. useEffect'in boş bağımlılık dizisi gibi çalışır (Bileşen doğduğunda)componentDidMount() {console.log("Hava durumu bileşeni ekrana yüklendi (Data çekilebilir).");}// 3. Olayları yöneten fonksiyondereceArtir = () => {this.setState({ derece: this.state.derece + 1 });};// 4. HTML/JSX çıktısını veren mecburi metotrender() {return (<div style={{ padding: "20px", border: "1px dashed gray" }}><h3>🌤️ Sınıf Tabanlı Hava Durumu</h3><p>{this.state.sehir} için sıcaklık: {this.state.derece}°C</p><button onClick={this.dereceArtir}>Sıcaklığı Artır</button></div>);}}
Harika! Aldığım onay ile birlikte 3. BÖLÜM: İLERİ SEVİYE REACT + REDUX dünyasına adım atıyoruz. Bu bölümde karmaşık durumları yönetmeyi, sayfalar arası geçiş yapmayı (yönlendirme) ve verileri tek bir merkezden tüm uygulamaya dağıtmayı öğreneceğiz.
useReducer
Hook'u
Birbiriyle ilişkili birden fazla
state olduğunda, durum değişikliklerini "aksiyon"
(action) adı verilen emirlerle tek bir merkezden yönetmek için
useReducer
kullanırız.
Aşağıdaki örnekte para yatırma, para çekme ve hesabı kapatma işlemlerini tek bir merkezden (reducer) yönetiyoruz.
import React, { useReducer } from "react";// 1. Başlangıç Durumu (Initial State)const baslangicDurumu = {bakiye: 0,aktif: true};// 2. Reducer Fonksiyonu (Merkezi Yönetim)function reducer(state, action) {// Eğer hesap aktif değilse (kapatıldıysa), hiçbir işlem yapmaif (!state.aktif && action.type !== "hesapAc") return state;switch (action.type) {case "hesapAc":return { ...state, aktif: true, bakiye: 500 }; // Hoş geldin bonusucase "paraYatir":return { ...state, bakiye: state.bakiye + action.payload };case "paraCek":if (state.bakiye < action.payload) {alert("Yetersiz bakiye!");return state;}return { ...state, bakiye: state.bakiye - action.payload };case "hesapKapat":if (state.bakiye !== 0) {alert("Hesabı kapatmak için önce bakiyenizi sıfırlamalısınız!");return state;}return { ...state, aktif: false };default:return state;}}// 3. Bileşen Dünyasıexport default function BankaUygulamasi() {const [state, dispatch] = useReducer(reducer, baslangicDurumu);return (<div style={{ padding: "20px", fontFamily: "sans-serif" }}><h2>🏦 useReducer ile Güvenli Banka</h2><p>Hesap Durumu: {state.aktif ? "🟢 Aktif" : "🔴 Kapalı"}</p><p>Mevcut Bakiye: <strong>{state.bakiye} TL</strong></p><div style={{ display: "flex", gap: "10px", marginTop: "10px" }}><button onClick={() => dispatch({ type: "hesapAc" })}>Hesap Aç</button><button onClick={() => dispatch({ type: "paraYatir", payload: 100 })}>100 TL Yatır</button><button onClick={() => dispatch({ type: "paraCek", payload: 50 })}>50 TL Çek</button><button onClick={() => dispatch({ type: "hesapKapat" })}>Hesabı Kapat</button></div></div>);}
Kullanıcı linklere tıkladığında tarayıcı yenilenmeden anında başka sayfaların açılmasını sağlar. URL üzerindeki parametreleri okuyarak dinamik sayfalar üretebiliriz.
(Bu yapının çalışması
için projeye npm
install react-router-dom
ile kütüphanenin kurulmuş olması gerekir.)
import React from "react";import { BrowserRouter, Routes, Route, Link, useParams } from "react-router-dom";// Sayfa 1: Ana Sayfafunction AnaSayfa() {return <h3>🏡 Ana Sayfaya Hoş Geldiniz!</h3>;}// Sayfa 2: Detay Sayfası (Dinamik Parametreli)function SehirDetay() {// URL'deki sehirAdi parametresini okuyoruz (Örn: /sehir/isparta)const { sehirAdi } = useParams();return <h3>📍 Gezilen Şehir Detayı: {sehirAdi.toUpperCase()}</h3>;}// Ana Uygulama Gövdesiexport default function RotaUygulamasi() {return (<BrowserRouter><nav style={{ display: "flex", gap: "15px", padding: "10px", background: "#eee" }}>{/* Sayfa yenilenmeden geçiş sağlayan Link etiketleri */}<Link to="/">Ana Sayfa</Link><Link to="/sehir/isparta">Isparta Detay</Link><Link to="/sehir/istanbul">İstanbul Detay</Link></nav><div style={{ padding: "20px" }}><Routes><Route path="/" element={<AnaSayfa />} />{/* İki nokta (:) işareti bu kısmın dinamik bir parametre olduğunu söyler */}<Route path="/sehir/:sehirAdi" element={<SehirDetay />} /></Routes></div></BrowserRouter>);}
Bileşen ağacında en üstten en alttaki bileşene, aradaki hiçbir bileşene uğramadan doğrudan veri transferi yapmak (Prop Drilling'i engellemek) için kullanılır.
import React, { createContext, useState, useContext } from "react";// 1. Context Deposunu Oluşturuyoruzconst TemaContext = createContext();// 2. Sağlayıcı (Provider) Bileşeniexport function TemaSaglayici({ children }) {const [tema, setTema] = useState("light");function temaDegistir() {setTema((mevcut) => (mevcut === "light" ? "dark" : "light"));}// Depodaki verileri ve fonksiyonları aşağıya yayınlıyoruzreturn (<TemaContext.Provider value={{ tema, temaDegistir }}>{children}</TemaContext.Provider>);}// 3. Tüketici Alt Bileşen (Mesafe ne kadar uzak olursa olsun veriyi doğrudan çeker)function IcerikKutusu() {// useContext kancası ile depoya bağlanıyoruzconst { tema, temaDegistir } = useContext(TemaContext);const kutuStili = {background: tema === "light" ? "#fff" : "#333",color: tema === "light" ? "#300" : "#fff",padding: "30px",borderRadius: "8px",marginTop: "10px"};return (<div style={kutuStili}><p>Şu anki tema modu: <strong>{tema.toUpperCase()}</strong></p><button onClick={temaDegistir}>Temayı Değiştir</button></div>);}// 4. Ana Uygulama Girişiexport function AppTemaUygulamasi() {return (// Tüm uygulamayı sarmalayarak depoyu erişilebilir kılıyoruz<TemaSaglayici><div style={{ padding: "20px" }}><h2>🌓 Context API Tema Deposu</h2><IcerikKutusu /></div></TemaSaglayici>);}
Büyük projelerde küresel state yönetimini daha performanslı, parçalara ayrılmış (slices) ve kurallı bir şekilde yapmak için kullanılan profesyonel endüstri standardıdır.
(Bu yapının çalışması
için @reduxjs/toolkit
ve react-redux
paketleri kurulmalıdır.)
import{createSlice,configureStore}from"@reduxjs/toolkit";// 1. Dilim (Slice) Oluşturma: Veriyi ve onun fonksiyonlarını bir arada tanımlıyoruzconstsayacDilimi= createSlice({name:"sayac",initialState: {deger:0},reducers: {artir: (state) => {// Redux Toolkit içinde state'i doğrudan mutasyona uğratabiliriz (Arka planda korur)state.deger +=1;},azalt: (state) => {state.deger -=1;},miktarlaArtir: (state,action) => {state.deger += action.payload;// Dışarıdan gelen veri payload içindedir}}});// Aksiyonları ve reducer'ı dışa aktarıyoruzexportconst{artir,azalt,miktarlaArtir} = sayacDilimi.actions;// 2. Merkezi Mağaza (Store) Kurulumuexportconststore= configureStore({reducer: {sayacDeposu: sayacDilimi.reducer}});
import React from "react";import { useSelector, useDispatch } from "react-redux";import { artir, azalt, miktarlaArtir } from "./sayacDilimi"; // Aksiyonları alıyoruzexport function ReduxSayacBileseni() {// useSelector ile merkezi depodaki istediğimiz veriyi çekiyoruzconst sayac = useSelector((state) => state.sayacDeposu.deger);// useDispatch yardımıyla depodaki fonksiyonları tetikliyoruz (aksiyon fırlatıyoruz)const dispatch = useDispatch();return (<div style={{ padding: "20px", border: "2px solid purple", borderRadius: "8px" }}><h3>⚛️ Redux Toolkit (RTK) Merkezi Sayacı</h3><p style={{ fontSize: "24px", fontWeight: "bold" }}>Sayaç: {sayac}</p><div style={{ display: "flex", gap: "10px" }}><button onClick={() => dispatch(artir())}>Artır (+1)</button><button onClick={() => dispatch(azalt())}>Azalt (-1)</button><button onClick={() => dispatch(miktarlaArtir(5))}>5 Ekle (+5)</button></div></div>);}
Harika! Aldığım onay ile birlikte 4. BÖLÜM: PROFESYONEL REACT GELİŞTİRME dünyasına giriş yapıyoruz. Bu bölüm tamamen büyük ölçekli, profesyonel şirket projelerinde kullanılan en güçlü kütüphaneleri ve kod tasarım mimarilerini içerir.
Modern React Router sürümünde,
bir sayfa daha ekrana çizilmeden önce (render öncesi) veriler
arkada hazırlanır. Buna loader
denir. Form gönderildiğinde ise sunucuya veri yazma işlemlerini
action
yönetir.
import React from "react";import { createBrowserRouter, RouterProvider, useLoaderData } from "react-router-dom";// 1. Veri Yükleyici Fonksiyon (Page Loader)// Sayfa açılmadan hemen önce bu fonksiyon çalışır ve veriyi hazırlarexport async function menuLoader() {// Gerçek projede fetch isteği atılır: fetch("https://api.com")const sahteUrunler = [{ id: 1, isim: "Margarita Pizza", fiyat: 150 },{ id: 2, isim: "Karışık Pizza", fiyat: 220 }];return sahteUrunler;}// 2. Sayfa Bileşenifunction MenuSayfasi() {// useLoaderData kancası yardımıyla yukarıdaki loader fonksiyonunun getirdiği veriyi okuyoruzconst pizzalar = useLoaderData();return (<div style={{ padding: "20px" }}><h3>🍕 Fast Pizza Menüsü</h3><ul>{pizzalar.map((pizza) => (<li key={pizza.id}>{pizza.isim} - <strong>{pizza.fiyat} TL</strong></li>))}</ul></div>);}// 3. Rota Tanımlamaları ve Eşleştirmeconst rotaSistemi = createBrowserRouter([{path: "/menu",element: <MenuSayfasi />,loader: menuLoader // Sayfa ile veri yükleyiciyi birbirine bağlıyoruz}]);export default function AppRouterYeni() {return <RouterProvider router={rotaSistemi} />;}
İnternetten gelen verileri tarayıcı hafızasında saklamak (caching), eskiyen verileri arka planda otomatik yenilemek ve yüklenme/hata durumlarını tek bir satırda yönetmek için kullanılan, günümüzün en popüler veri çekme kütüphanesidir.
(Bu yapının çalışması
için @tanstack/react-query
kütüphanesi kurulmalıdır.)
import React from "react";import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";// Sahte veri tabanı servis fonksiyonları (Supabase veya başka bir API olduğunu varsayalım)async function odalariGetir() {return [{ id: 101, odaAdi: "Kral Dairesi", kapasite: 2 },{ id: 102, odaAdi: "Aile Odası - Manzaralı", kapasite: 4 }];}async function odaSilServisi(id) {console.log(`${id} numaralı oda veri tabanından silindi.`);return id;}export default function OtelYonetimPaneli() {const queryClient = useQueryClient();// 1. VERİ ÇEKME (Query): Odaları internetten indirir ve hafızaya (cache) kaydederconst { data: odalar, isLoading, isError } = useQuery({queryKey: ["odalar"], // Hafızadaki benzersiz anahtar kelimequeryFn: odalariGetir // Veriyi çeken fonksiyon});// 2. VERİ DEĞİŞTİRME (Mutation): Silme, ekleme veya güncelleme işlemleri için kullanılırconst { mutate: odaSil, isLoading: siliniyor } = useMutation({mutationFn: odaSilServisi,onSuccess: () => {// Oda başarıyla silindiğinde hafızadaki eski listeyi geçersiz (stale) ilan et!// Böylece React Query arka planda listeyi otomatik olarak güncel haliyle yeniden çeker.queryClient.invalidateQueries({ queryKey: ["odalar"] });alert("Oda başarıyla silindi ve liste yenilendi!");}});if (isLoading) return <p>🏨 Otel verileri yükleniyor...</p>;if (isError) return <p>❌ Veriler alınırken bir hata oluştu!</p>;return (<div style={{ padding: "20px", fontFamily: "sans-serif" }}><h3>🌲 The Wild Oasis - Oda Yönetim Paneli</h3><ul>{odalar.map((oda) => (<li key={oda.id} style={{ margin: "10px 0" }}><strong>{oda.odaAdi}</strong> (Maksimum {oda.kapasite} Kişilik)<buttononClick={() => odaSil(oda.id)}disabled={siliniyor}style={{ marginLeft: "15px", color: "white", background: "red", border: "none", padding: "5px 10px", cursor: "pointer" }}>Sil</button></li>))}</ul></div>);}
Birbiriyle sıkı sıkıya bağlı
çalışan alt bileşenleri tek bir çatı altında toplayarak,
dışarıdan kullanan kişiye muazzam bir esneklik sağlayan
profesyonel mimari kalıptır. HTML'deki <select>
ve <option>
etiketlerinin birbiriyle konuşması mantığına benzer.
import React, { createContext, useState, useContext } from "react";// 1. Ortak bir Context iletişimi kuruyoruzconst KartContext = createContext();// 2. Çatı Bileşen (Parent)function Kart({ children }) {const [acik, setAcik] = useState(false);const toggle = () => setAcik((mevcut) => !mevcut);return (<KartContext.Provider value={{ acik, toggle }}><div style={{ border: "1px solid #ddd", borderRadius: "6px", margin: "10px", padding: "10px", width: "300px" }}>{children}</div></KartContext.Provider>);}// 3. Başlık Bileşeni (Tıklandığında açıp kapatacak buton)function Baslik({ children }) {const { toggle } = useContext(KartContext);return (<div onClick={toggle} style={{ cursor: "pointer", fontWeight: "bold", background: "#f0f0f0", padding: "8px" }}>{children} (Tıkla Aç/Kapat)</div>);}// 4. İçerik Bileşeni (Açık ise görünecek kısım)function Icerik({ children }) {const { acik } = useContext(KartContext);if (!acik) return null; // Kapalıysa ekrana hiçbir şey çizmereturn <div style={{ padding: "10px", background: "#fafafa" }}>{children}</div>;}// 5. Alt parçaları ana çatıya (Kart nesnesine) bağlıyoruz (Compound pattern kuralı)Kart.Baslik = Baslik;Kart.Icerik = Icerik;// --- BU KALIBIN DIŞARIDAN KULLANIMI ---export function AppCompoundPattern() {return (<div style={{ padding: "20px" }}><h3>🧩 Compound Component Tasarım Kalıbı</h3>{/* Son derece esnek ve okunabilir bir hiyerarşi sağlıyor */}<Kart><Kart.Baslik>Gizlilik Sözleşmesi</Kart.Baslik><Kart.Icerik>Bu alanda şirket politikaları ve kullanıcı haklarına dair detaylı açıklamalar yer almaktadır.</Kart.Icerik></Kart><Kart><Kart.Baslik>İletişim Bilgileri</Kart.Baslik><Kart.Icerik>E-posta: destek@otelimiz.com <br /> Telefon: 0246 211 00 00</Kart.Icerik></Kart></div>);}
Harika! Aldığım onay ile birlikte bu muazzam eğitim serüveninin son ve en modern adımı olan 5. BÖLÜM: NEXT.JS İLE FULL-STACK REACT dünyasına giriş yapıyoruz.
Next.js dünyasında kodlar artık sadece kullanıcının tarayıcısında (Client) değil, doğrudan sunucuda (Server) çalışır. Bu bölümde Sunucu Bileşenlerini (RSC), Sunucu Fonksiyonlarını (Server Actions) ve modern veri akış mimarilerini kodlayarak öğreneceğiz.
Next.js (App Router) mimarisinde,
bileşenler varsayılan olarak Sunucu Bileşeni
olarak çalışır. Bu sayede useEffect
veya harici bir state yönetimi kullanmadan, fonksiyonun kendisini
async
yaparak doğrudan veri tabanına veya API'ye istek atabiliriz. Kodlar
tarayıcıya hiç yük bindirmeden sunucuda çalışıp biter.
// src/app/kabine/page.js// Bu fonksiyon doğrudan sunucu tarafında çalışır.// Tarayıcı bu fonksiyonun içindeki kodları veya şifreleri asla göremez!async function kabinVerileriniGetir() {// Gerçek projede doğrudan Supabase veya SQL veri tabanına sorgu atılabilir.// Örnek bir API isteği simülasyonu yapıyoruz:const yanıt = await fetch("https://thewildoasis.com", {// Next.js Önbellek Yönetimi (Caching): Veriyi otomatik hafızaya al ama her 1 saatte bir yenile (ISR)next: { revalidate: 3600 }});if (!yanıt.ok) throw new Error("Kabin verileri yüklenemedi!");return yanıt.json();}// Bileşenin kendisi bir async fonksiyondur!export default async function KabinlerSayfasi() {const kabinler = await kabinVerileriniGetir();return (<div style={{ padding: "30px", fontFamily: "sans-serif" }}><h2>🌲 The Wild Oasis Müşteri Web Sitesi</h2><p>Sunucudan (SSR) anlık olarak hazırlanan kabin listemiz:</p><div style={{ display: "grid", gap: "20px", marginTop: "20px" }}>{kabinler.map((kabin) => (<div key={kabin.id} style={{ border: "1px solid #ddd", padding: "15px", borderRadius: "8px" }}><h4>{kabin.name}</h4><p>Maksimum Kapasite: {kabin.maxCapacity} Kişi</p><span style={{ fontWeight: "bold", color: "green" }}>{kabin.regularPrice} TL / Gecelik</span></div>))}</div></div>);}
Eğer yukarıdaki gibi sunucudan
veri çekerken sayfanın bir kısmı çok yavaş yükleniyorsa, tüm
sayfanın açılmasını beklemek yerine hızlı yerleri anında
gösterip yavaş yeri Suspense
ile sarmalayarak bekletebiliriz.
import React, { Suspense } from "react";import YavasYüklenenKabinListesi from "./KabinListesi"; // Ağır bir veri çekme işlemi içeriyorfunction YükleniyorAnimasyonu() {return <p style={{ color: "orange" }}>⏳ Kabin listesi sunucudan yükleniyor, lütfen bekleyin...</p>;}export default function DetayliKabinSayfasi() {return (<div style={{ padding: "20px" }}>{/* Bu kısım anında ekrana çizilir, kullanıcıyı bekletmez */}<h3>🏡 Konaklama Seçeneklerimiz</h3><p>Aşağıdaki listeden dilediğiniz lüks odayı seçebilirsiniz:</p>{/* Yavaş yüklenen kısım Suspense içine alınır */}<Suspense fallback={<YükleniyorAnimasyonu />}><YavasYüklenenKabinListesi /></Suspense></div>);}
Next.js içindeki en devrimsel
özelliklerden biridir. Form verilerini sunucuya göndermek için
harici bir API rotası (POST
isteği vb.) yazmamıza gerek kalmaz. Doğrudan formun action
özelliğine sunucuda çalışacak bir fonksiyon bağlarız.
// src/app/profil/actions.js"use server"; // Bu dosyadaki tüm fonksiyonların sadece sunucuda çalışacağını garanti eder// Sunucu üzerinde çalışacak fonksiyonexport async function profilGuncelleAction(formData) {const isim = formData.get("kullaniciIsim");const eposta = formData.get("kullaniciEposta");// Burada doğrudan veri tabanı güncelleme kodu çalışır:// await db.user.update({ isim, eposta })console.log(`Sunucu Başarıyla Güncellendi: ${isim} - ${eposta}`);// Önbelleği temizle ve sayfayı yeni verilerle tazeleyip güncelle (Revalidation)// revalidatePath("/profil");}
Form tarafında yüklenme
durumlarını ve anlık iyimser güncellemeleri (useOptimistic)
yönetmek için modern React kancalarını kullanırız.
// src/app/profil/page.js"use client"; // Form interaktif olacağı (state içereceği) için istemci bileşeni yapıyoruzimport React, { useOptimistic } from "react";import { profilGuncelleAction } from "./actions"; // Sunucu fonksiyonunu çağırıyoruzexport default function ProfilFormu() {// useOptimistic Kancası: Veri tabanından onay gelmeden önce ekrandaki yazıyı// anında güncelleyerek kullanıcıya kusursuz ve çok hızlı bir deneyim sunar.const [iyimserIsim, setIyimserIsim] = useOptimistic("Ahmet Yılmaz", // Başlangıç değeri(mevcutDurum, yeniIsim) => yeniIsim // Güncelleme mantığı);async function handleFormGonder(formData) {const girilenIsim = formData.get("kullaniciIsim");// 1. Veri tabanına gitmeden EKRAI ANINDA GÜNCELLE (İyimser olarak)setIyimserIsim(girilenIsim);// 2. Arka planda sunucu eylemini tetikle ve veri tabanına yazawait profilGuncelleAction(formData);alert("Profil bilgileriniz sunucuda başarıyla güncellendi!");}return (<div style={{ padding: "25px", maxWidth: "400px" }}><h3>👤 Profil Ayarlarım</h3><p>Mevcut Profil Adı: <strong>{iyimserIsim}</strong></p>{/* action özelliğine doğrudan sunucu fonksiyonunu bağlıyoruz */}<form action={handleFormGonder} style={{ display: "flex", flexDirection: "column", gap: "10px", marginTop: "15px" }}><inputtype="text"name="kullaniciIsim"placeholder="Yeni adınızı yazın..."requiredstyle={{ padding: "8px" }}/><inputtype="email"name="kullaniciEposta"placeholder="E-posta adresiniz..."requiredstyle={{ padding: "8px" }}/><button type="submit" style={{ padding: "10px", background: "blue", color: "white", border: "none", cursor: "pointer" }}>Bilgileri Güvenle Kaydet</button></form></div>);}
Bu harika bir ileri seviye hamlesi! Artık temel yapıları geride bıraktık ve kıdemli (Senior) bir yazılımcı gibi düşünerek projelerimizi büyütme, kurumsal mimari tasarlama ve iş mülakatlarında fark yaratma aşamasına geldik.
İstediğiniz bu devasa çalışmayı 3 ana modül halinde, en ince detayına kadar adım adım kurguluyoruz.
Önceki adımlarda yazdığımız temel uygulamalara kurumsal seviyede yeni özellikler, performans optimizasyonları ve hata yönetimleri ekliyoruz.
Mevcut listemize eşyaların kategorisini (Giysi, Teknoloji, İlaç vb.) ekliyoruz ve kullanıcının bu eşyaları isme, alınma durumuna veya eklenme sırasına göre filtreleyebilmesini sağlıyoruz.
import React, { useState } from "react";export default function GelismisSeyahatUygulamasi() {const [esyalari, setEsyalari] = useState([]);const [filtre, setFiltre] = useState("all"); // all, packed, unpackedconst [sirala, setSirala] = useState("input"); // input, description, packedStatusfunction handleEsyaEkle(yeniEsya) {setEsyalari((eski) => [...eski, yeniEsya]);}function handleDurumDegistir(id) {setEsyalari((eski) =>eski.map((e) => (e.id === id ? { ...e, alindi: !e.alindi } : e)));}// 1. FİLTRELEME MANTIĞIlet filtrelenmisEsyalar = esyalari;if (filtre === "packed") filtrelenmisEsyalar = esyalari.filter((e) => e.alindi);if (filtre === "unpacked") filtrelenmisEsyalar = esyalari.filter((e) => !e.alindi);// 2. SIRALAMA MANTIĞI (Immutable Array Operation)let siralanmisEsyalar = [...filtrelenmisEsyalar];if (sirala === "description") {siralanmisEsyalar.sort((a, b) => a.tanim.localeCompare(b.tanim));}if (sirala === "packedStatus") {siralanmisEsyalar.sort((a, b) => Number(a.alindi) - Number(b.alindi));}return (<div style={{ padding: "20px", background: "#f4f6f8" }}><h2>🧳 Kurumsal Seyahat Listesi v2</h2><GelişmişForm onEsyaEkle={handleEsyaEkle} />{/* Kontrol Paneli */}<div style={{ margin: "15px 0", display: "flex", gap: "10px" }}><select value={filtre} onChange={(e) => setFiltre(e.target.value)}><option value="all">Hepsini Göster</option><option value="packed">Sadece Alınanlar</option><option value="unpacked">Eksik Olanlar</option></select><select value={sirala} onChange={(e) => setSirala(e.target.value)}><option value="input">Eklenme Sırasına Göre</option><option value="description">Alfabetik Sırala</option><option value="packedStatus">Alınma Durumuna Göre</option></select></div><ul>{siralanmisEsyalar.map((esya) => (<li key={esya.id} style={{ textDecoration: esya.alindi ? "line-through" : "none" }}><input type="checkbox" checked={esya.alindi} onChange={() => handleDurumDegistir(esya.id)} />[{esya.kategori}] {esya.tanim}</li>))}</ul></div>);}function GelişmişForm({ onEsyaEkle }) {const [tanim, setTanim] = useState("");const [kategori, setKategori] = useState("Giysi");function handleSubmit(e) {e.preventDefault();if (!tanim) return;onEsyaEkle({ id: Date.now(), tanim, kategori, alindi: false });setTanim("");}return (<form onSubmit={handleSubmit}><input value={tanim} onChange={(e) => setTanim(e.target.value)} placeholder="Eşya..." /><select value={kategori} onChange={(e) => setKategori(e.target.value)}><option value="Giysi">Giysi</option><option value="Teknoloji">Teknoloji</option><option value="Mutfak">Mutfak</option></select><button type="submit">Ekle</button></form>);}
Film listemizde kullanıcı alta indikçe yeni sayfaları çeken bir yapıyı ve hata aldığında tüm uygulamanın çökmesini engelleyen Error Boundary mantığını kuruyoruz.
import React, { useState, useEffect, useRef } from "react";export function GelişmişFilmUygulamasi() {const [filmler, setFilmler] = useState([]);const [sayfa, setSayfa] = useState(1);const [yukleniyor, setYukleniyor] = useState(false);const listeSonuRef = useRef(null);// Sayfa numarası arttıkça yeni filmleri listeye ekleyen mimariuseEffect(() => {async function dahaFazlaFilmGetir() {setYukleniyor(true);// Gerçek projede: fetch(`...&page=${sayfa}`)setTimeout(() => {const yeniGelenler = [{ id: Math.random(), Title: `Film Paketi ${sayfa} - A`, Year: "2026" },{ id: Math.random(), Title: `Film Paketi ${sayfa} - B`, Year: "2026" },];setFilmler((eski) => [...eski, yeniGelenler]);setYukleniyor(false);}, 1000);}dahaFazlaFilmGetir();}, [sayfa]);// Intersection Observer API ile listenin sonuna gelindiğini yakalama (Infinite Scroll)useEffect(() => {const observer = new IntersectionObserver((entries) => {if (entries[0].isIntersecting && !yukleniyor) {setSayfa((eskiSayfa) => eskiSayfa + 1);}},{ threshold: 1.0 });if (listeSonuRef.current) observer.observe(listeSonuRef.current);return () => observer.disconnect();}, [yukleniyor]);return (<div style={{ padding: "20px", height: "400px", overflowY: "scroll" }}><h3>Sonsuz Film Listesi (Infinite Scroll)</h3>{filmler.map((film) => (<div key={film.id} style={{ padding: "20px", borderBottom: "1px solid #eee" }}><h4>{film.Title}</h4></div>))}{/* Tetikleyici Eleman */}<div ref={listeSonuRef} style={{ height: "20px", background: "transparent" }} />{yukleniyor && <p>🔄 Arka planda yeni filmler yükleniyor...</p>}</div>);}
Büyük teknoloji şirketlerinin teknik mülakatlarında (Technical Interview) en sık sorduğu kıdemli React soruları ve verilmesi gereken profesyonel kod cevapları:
useEffect
içinde veri çekerken (Data Fetching) oluşan "Race Condition"
nedir? Nasıl çözülür?Sorun Açıklaması: Kullanıcı arama kutusuna çok hızlı bir şekilde sırasıyla "A", sonra "AB" yazar. İki adet internet isteği gider. Eğer ilk giden istek ("A") internet yavaşlığından dolayı ikinci istekten ("AB") daha geç sonuçlanırsa, ekranda en son yazılan değil, yanlışlıkla eski aramanın sonuçları kalır. Buna Race Condition (Yarış Durumu) denir.
Pratik Çözüm: useEffect
içindeki temizleme (cleanup) fonksiyonunda bir bayrak (flag) veya
AbortController
kullanmaktır.
useEffect(() => {let aktif = true;async function veriGetir() {const sonuc = await fetch(`https://api.com{arama}`);const veri = await sonuc.json();// Eğer bileşen güncelliğini yitirdiyse (kullanıcı yeni bir harfe bastıysa)// state güncellemeyi iptal et!if (aktif) {setSonuclar(veri);}}veriGetir();// Temizleme fonksiyonu: Yeni harfe basıldığında bir önceki efekti geçersiz kılarreturn () => {aktif = false;};}, [arama]);
useMemo
ve useCallback
tam olarak ne zaman kullanılmalıdır? Gereksiz kullanımların
zararı nedir?Cevap:
useMemo: Ağır matematiksel veya algoritmik hesaplamaların (örneğin 10.000 elemanlı bir diziyi filtreleyip sıralamak) her render işleminde baştan çalışmasını engellemek için değeri hafızaya alır.
useCallback: Alt
bileşenlere prop olarak gönderilen fonksiyonların referans
adreslerini hafızada sabit tutar. Böylece alt bileşenin gereksiz
yere tekrar çizilmesini (React.memo
ile birlikte) engeller.
Zararı: Her iki kanca da arka planda ekstra bellek harcar ve bağımlılık dizisini kontrol etmek için işlemciyi yorar. Basit hesaplamalarda veya normal fonksiyonlarda kullanılması uygulamayı hızlandırmaz, aksine yavaşlatır.
Cevap: Bileşen
Kompozisyonu (Component Composition)
ile çözülebilir. Veriyi aradaki bileşenlere teslim etmek yerine,
en alttaki bileşeni en üstte (baba evinde) oluşturup aradakilere
doğrudan children
veya bir prop nesnesi olarak aktarabiliriz.
// Prop Drilling Yaşatan Kötü Tasarım:<BüyükBaba veri={veri}> -> <Baba veri={veri}> -> <Torun veri={veri}>// Mimariden Context İstemeyen Akıllı Çözüm (Composition):function BüyükBaba() {const [veri] = useState("Gizli Bilgi");return (<Baba><Torun veri={veri} /></Baba>);}
Büyük ölçekli, ekiplerin (Enterprise) rahatça çalışabileceği, bakımı kolay (Maintainable) modern bir React/Next.js projesinin endüstri standardı klasör yapısı ve mimari katmanları şu şekildedir:
my-enterprise-app/├── public/ # Statik dosyalar (Logolar, ikonlar, fontlar)├── src/│ ├── assets/ # Global resimler ve ortak stil şablonları│ ├── components/ # 1. KÜRESEL VE BAĞIMSIZ BİLEŞENLER (Atomik Yapı)│ │ ├── ui/ # Temel tasarım elementleri (Buton, Input, Modal)│ │ │ ├── Button.jsx│ │ │ └── Input.jsx│ │ ├── feedback/ # Bildirim ve yükleniyor bileşenleri│ │ └── layout/ # Sayfa iskeletleri (Navbar, Sidebar, Footer)│ ├── context/ # Küresel hafif durum depoları (Tema, Dil seçimi)│ ├── features/ # 2. ÖZELLİK TABANLI MODÜLLER (Feature-Driven)│ │ ├── authentication/ # Giriş / Çıkış ile ilgili her şey tek bir klasörde!│ │ │ ├── components/ # Sadece buraya ait alt bileşenler (LoginForm.jsx)│ │ │ ├── hooks/ # Sadece buraya ait özel kancalar (useAuth.js)│ │ │ ├── services/ # Kimlik doğrulama API istekleri (authApi.js)│ │ │ └── authSlice.js # Redux Toolkit State dilimi│ │ ├── cabins/ # Otel Odaları Yönetim Modülü│ │ └── bookings/ # Rezervasyon Yönetim Modülü│ ├── hooks/ # Küresel ortak kancalar (useLocalStorage, useClickOutside)│ ├── pages/ # Sayfa giriş noktaları (Yönlendirici rotaları için)│ ├── services/ # Küresel API servis istemcileri (Axios veya Supabase istemcisi)│ │ ├── supabase.js│ │ └── apiCore.js│ ├── store/ # 3. MERKEZİ REDUX / GLOBAL DURUM MAĞAZASI│ │ └── store.js│ ├── utils/ # Yardımcı saf fonksiyonlar (Tarih formatlama, Para birimi çevirici)│ │ └── helpers.js│ ├── App.jsx # Ana uygulama giriş rotası ve sağlayıcılar│ └── main.jsx # React'i tarayıcıya bağlayan kök dosya
Feature-Driven
(Özellik Odaklı) Geliştirme: Bileşenleri tipine göre (tüm
hooks'lar tek yerde, tüm sayfalar tek yerde) dağıtmak yerine, iş
mantığına göre (features/)
gruplayın. Bir yazılımcı "Kabin Silme" özelliğini
değiştireceği zaman sadece features/cabins/
klasörüne odaklanır. Başka hiçbir yere bakmasına gerek kalmaz.
Strict
Layering (Keskin Katman Kuralı): Görsel arayüz (JSX), iş
mantığından (Business Logic) tamamen ayrılmalıdır. Bileşen
içinde doğrudan büyük API istekleri yazılmamalıdır. İstekler
services/
altındaki dosyalarda yapılır, bileşen bu fonksiyonları çağırır.
UI Isolation (Tasarım
Bağımsızlığı): components/ui/
altındaki temel butonlar, girdi alanları asla bir iş mantığı
(örneğin kullanıcı bilgisi, sepet durumu) içermemelidir. Sadece
aldıkları props
ile saf bir şekilde ekrana çizilmeli, böylece projenin her
yerinde güvenle yeniden kullanılabilmelidir.
*************************************************************************************************************************