1. "Öğrenecekleriniz" Bölümünün Açıklaması

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ı:


2. Kurs Konularının Detaylı İncelemesi, Örnekler ve Uygulamalar

Giriş & JSX Yapısı

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.

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 (Bileşen) ve Props Yapısı

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.

// UrunKarti adında bir bileşen (Component) oluşturuyoruz
function 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ıyoruz
function 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;

Hooks (useState ve useEffect)

import React, { useState, useEffect } from 'react';

function SayacUygulaması() {
  // sayi adında bir durum (state) oluşturduk, ilk değeri 0
  const [sayi, setSayi] = useState(0);

  // Sayfa ilk yüklendiğinde bir kere çalışır
  useEffect(() => {
    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ı;

Axios ile HTTP İstekleri (GET, POST, PUT, DELETE)

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.

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;

Redux Toolkit (Merkezi Durum Yönetimi)

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.

// sepetSlice.js (Redux yapısının kurulması)
import { createSlice } from '@reduxjs/toolkit';

const sepetSlice = createSlice({
  name: 'sepet',
  initialState: { urunler: [], toplamTutar: 0 },
  reducers: {
    sepeteEkle: (state, action) => {
      state.urunler.push(action.payload);
      state.toplamTutar += action.payload.fiyat;
    }
  }
});

export const { sepeteEkle } = sepetSlice.actions;
export default sepetSlice.reducer;

React Router Dom (Sayfa Yönlendirmeleri)

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.

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;

TypeScript Entegrasyonu

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.

// TypeScript ile bir objenin yapısını (tipini) tanımlıyoruz
interface Görev {
  id: number;
  baslik: string;
  tamamlandi mi: boolean;
}

// Fonksiyonumuzun sadece bu tipte veri kabul etmesini sağlıyoruz
function GorevGoster(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ı!

Formik ve YUP ile Form Yönetimi

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.

import * as Yup from 'yup';

// Kayıt formu için geçerlilik kuralları hazırlıyoruz
const kayitSemasi = 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!')
});

Material UI (MUI) Kütüphanesi

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.

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 ile Kullanıcı İşlemleri

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.

import { getAuth, signInWithEmailAndPassword } from "firebase/auth";

const auth = getAuth();

function KullaniciGiris(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);
    });
}

3. Kurstaki Uygulamalı Projeler

Kurs boyunca öğrendiğiniz bu teorik bilgileri pekiştirmek amacıyla sıfırdan şu projeleri geliştireceksiniz:

  1. Kurslarım Projesi: Kart tasarımları yapmayı, dizi içerisindeki kurs verilerini map fonksiyonu ile ekranda listelemeyi ve temel komponent mimarisini kavramayı sağlar.

  2. 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.

  3. 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.

  4. 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.

  5. 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.


1. Kurslarım Projesi (Temel Mimari)

Bu proje, React'ın en temel yapı taşı olan "Bileşenlere Bölme (Componentization)" ve "Props ile Veri Taşıma" mantığını öğretir.

Klasör Yapısı

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

Veri Akışı ve Kod Mantığı

// KursListesi.jsx içindeki temel döngü mimarisi
import 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>
  );
}

2. Döviz Kuru Projesi (API Entegrasyonu)

Bu proje, dış dünyadaki gerçek bir sunucuyla (API) asenkron iletişim kurmayı ve form girdilerini anlık olarak işlemeyi öğretir.

Klasör Yapısı

src/
├── components/
│   └── DovizCevirici.jsx  // Input ve Select kutularının olduğu ana arayüz
├── App.jsx
└── index.css

Veri Akışı ve Kod Mantığı

// DovizCevirici.jsx içindeki HTTP istek mimarisi
import { 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);
  };
}

3. Todo Projesi (Yerel Durum ile CRUD İşlemleri)

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.

Klasör Yapısı

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

Veri Akışı ve Kod Mantığı

// App.jsx içindeki CRUD fonksiyon mimarisi
const [todos, setTodos] = useState([]);

const todoEkle = (yeniTodo) => {
  setTodos([...todos, yeniTodo]); // State'e güvenli ekleme
};

const todoSil = (id) => {
  setTodos(todos.filter(todo => todo.id !== id)); // Listeden temizleme
};

const todoGuncelle = (id, yeniYazi) => {
  setTodos(todos.map(todo => todo.id === id ? { ...todo, title: yeniYazi } : todo));
};

4. Redux + TypeScript Todo Projesi (Kurumsal Mimari)

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.

Klasör Yapısı

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

Veri Akışı ve Kod Mantığı

// todoSlice.ts içindeki TypeScript ve Redux Toolkit mimarisi
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { Todo, TodoInitialState } from '../types/todoTypes';

const initialState: TodoInitialState = { todos: [] };

export const todoSlice = 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);
    }
  }
});

5. Gelişmiş E-Ticaret Projesi (Tam Donanımlı Üretim Mimarisi)

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.

Klasör Yapısı

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

Veri Akışı ve İleri Seviye Mantıklar

  1. 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.

  2. 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.

  3. 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.

  4. 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 mimarisi
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

const initialState = {
  cartItems: JSON.parse(localStorage.getItem('sepet') || '[]'), // Hafızadan geri yükleme
  totalAmount: 0
};

export const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    addToCart: (state, action) => {
      // Ürün zaten sepette var mı kontrolü
      const findItem = 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 hesapla
      state.totalAmount = state.cartItems.reduce((total, item) => total + (item.price * item.quantity), 0);
      // LocalStorage güncelle
      localStorage.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.


1. 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.

Mimari Akış Şeması

  1. Bileşen (Component): Sayfa açıldığında bir asenkron aksiyon fırlatır (dispatch(getProducts())).

  2. Thunk (Ara Yazılım): Arka planda API'ye istek (HTTP Request) atar ve yanıtı bekler.

  3. ExtraReducers (Durum Yakalayıcı): İsteğin o anki durumuna göre (pending, fulfilled, rejected) merkezi state yapısını günceller.

Kod Mimarisi (productSlice.ts)

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

// 1. Ürün Nesnesinin Tip Tanımı
export interface Product {
  id: number;
  title: string;
  price: number;
  description: string;
  image: string;
}

// 2. Product Slice İçin State Tip Tanımı
interface ProductState {
  products: Product[];
  loading: boolean;
  error: string | null;
}

const initialState: ProductState = {
  products: [],
  loading: false,
  error: null,
};

// 3. Asenkron Thunk Aksiyonu Tanımlama
// İlk parametre aksiyonun adıdır, ikinci parametre asenkron fonksiyondur.
export const fetchProducts = createAsyncThunk(
  'products/fetchProducts',
  async (_, thunkAPI) => {
    try {
      const response = await axios.get<Product[]>('https://fakestoreapi.com');
      return response.data; // Bu veri 'fulfilled' durumunda action.payload olur.
    } catch (error: any) {
      return thunkAPI.rejectWithValue(error.message || 'Ürünler yüklenirken bir hata oluştu.');
    }
  }
);

// 4. Slice Tanımı ve ExtraReducers Kullanımı
export const productSlice = createSlice({
  name: 'products',
  initialState,
  reducers: {}, // Senkron aksiyonlar buraya yazılır
  extraReducers: (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.payload as string;
      });
  },
});

export default productSlice.reducer;

Bileşende Kullanımı (Home.tsx)

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchProducts } from '../slices/productSlice';
import { RootState, AppDispatch } from '../app/store';

function Home() {
  // TypeScript projelerinde normal useDispatch yerine özel tiplendirilmiş AppDispatch kullanılır
  const dispatch = useDispatch<AppDispatch>();
  
  // Merkezi depodan verileri çekme
  const { 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>
  );
}

2. Store, Slice ve Dispatch Üçlüsünün İletişim Mantığı

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)  │
└─────────────────┘                              └────────────────────┘

1. Slice (Fabrika Modülü)

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.

2. Dispatch (Postacı/Tetikleyici)

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.

3. Store (Merkezi Banka/Depo)

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.

İletişim Örneği (Sepete Ürün Ekleme Akışı)

  1. 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));
  2. Adım (Döngü): dispatch bu emri alır, içinde taşınan veriyi (payload: secilenUrun) ilgili Slice'a ulaştırır.

  3. Adım (Slice): Slice içindeki addToCart fonksiyonu tetiklenir. Gelen ürünü mevcut sepet dizisine güvenli bir şekilde ekler.

  4. 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.


3. TypeScript Projelerinde Tip (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?

Uygulamalı Örnekler

1. 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ü
interface User {
  id: string;
  name: string;
  email: string;
}

// Admin Kullanıcısı (Normal kullanıcının tüm özelliklerine sahip + ekstra yetki barındırır)
interface AdminUser extends User {
  role: 'superadmin' | 'manager';
  permissions: string[];
}

// Kullanım Örneği
const aktifYonetici: AdminUser = {
  id: "usr_102",
  name: "Can",
  email: "can@firma.com",
  role: "manager",
  permissions: ["delete_product", "edit_user"]
};

2. 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 olabilir
type OrderStatus = 'hazirlaniyor' | 'kargoda' | 'teslim-edildi';

// Fonksiyon Tipi Tanımlama: İki sayı alıp geriye sayı döndüren bir fonksiyon şablonu
type MatematikFonksiyonu = (a: number, b: number) => number;

const toplamaIslemi: MatematikFonksiyonu = (x, y) => x + y;

// Nesne Tiplerini Birleştirme (Intersection)
type Kisi = { isim: string };
type Calisan = Kisi & { maas: number };

const personel: Calisan = {
  isim: "Ayşe",
  maas: 45000
};

3. React Bileşenlerinde Props Tiplendirmesi

Bileşenlerimize dışarıdan veri gönderirken hata yapmamak için Props yapılarını tiplendiririz.

import React from 'react';

// Bileşenin alacağı Props yapısını tanımlıyoruz
interface CustomButtonProps {
  label: string;
  color?: 'primary' | 'secondary'; // Soru işareti (?) bu özelliğin zorunlu olmadığını (opsiyonel) belirtir
  onClick: () => void; // Parametre almayan ve bir şey döndürmeyen fonksiyon tipi
}

// React.FC (Functional Component) kullanarak bileşene tipleri bağlıyoruz
export const CustomButton: 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.


1. Tek Parça TypeScript + Redux Toolkit Mini Projesi

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.

import React from 'react';
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { Provider, useDispatch, useSelector } from 'react-redux';

// ==========================================
// 1. TYPESCRIPT TIP TANIMLAMALARI (TYPES)
// ==========================================
export interface Kitap {
  id: string;
  baslik: string;
  yazar: string;
  okundu: boolean;
}

interface KitaplikState {
  kitaplar: Kitap[];
}

// Sahte Başlangıç Verileri
const initialState: 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
// ==========================================
const kitaplikSlice = 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>) => {
      const kitap = state.kitaplar.find(k => k.id === action.payload);
      if (kitap) {
        kitap.okundu = !kitap.okundu;
      }
    },
    // Kitap silme aksiyonu
    kitapSil: (state, action: PayloadAction<string>) => {
      state.kitaplar = state.kitaplar.filter(k => k.id !== action.payload);
    }
  }
});

// Aksiyonları dışarı aktarıyoruz
export const { kitapEkle, durumDegistir, kitapSil } = kitaplikSlice.actions;

// ==========================================
// 3. CENTRAL STORE (MERKEZİ DEPO) AYARI
// ==========================================
export const store = configureStore({
  reducer: {
    kitaplik: kitaplikSlice.reducer
  }
});

// TypeScript için Store üzerinden otomatik tip çıkarma (Type Inference)
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

// ==========================================
// 4. REACT BİLEŞENİ (COMPONENT & UI)
// ==========================================
function KitaplikUygulamasi() {
  // Merkezi depodan verileri TypeScript güvenliği ile çekiyoruz
  const kitaplar = useSelector((state: RootState) => state.kitaplik.kitaplar);
  const dispatch = useDispatch<AppDispatch>();

  // Form girdileri için yerel state
  const [baslik, setBaslik] = React.useState('');
  const [yazar, setYazar] = React.useState('');

  const handleEkle = (e: React.FormEvent) => {
    e.preventDefault();
    if (!baslik.trim() || !yazar.trim()) return;

    // Yeni kitap nesnemizi kurallara (Kitap tipine) uygun oluşturuyoruz
    const yeniKitap: Kitap = {
      id: Date.now().toString(),
      baslik,
      yazar,
      okundu: false
    };

    // Store'a gönderiyoruz (Dispatch)
    dispatch(kitapEkle(yeniKitap));

    // Inputları temizle
    setBaslik('');
    setYazar('');
  };

  return (
    <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif', maxWidth: '500px', margin: '0 auto' }}>
      <h2>📚 Küçük Kitaplığım (TS + Redux)</h2>
      
      {/* Kitap Ekleme Formu */}
      <form onSubmit={handleEkle} style={{ marginBottom: '20px', display: 'flex', flexDirection: 'column', gap: '8px' }}>
        <input 
          type="text" 
          placeholder="Kitap Adı" 
          value={baslik} 
          onChange={(e) => setBaslik(e.target.value)}
          style={{ padding: '8px' }}
        />
        <input 
          type="text" 
          placeholder="Yazar Adı" 
          value={yazar} 
          onChange={(e) => setYazar(e.target.value)}
          style={{ padding: '8px' }}
        />
        <button type="submit" style={{ padding: '8px', backgroundColor: '#007bff', color: 'white', border: 'none', cursor: 'pointer' }}>
          Kitaplığa Ekle
        </button>
      </form>

      {/* Kitap Listesi */}
      <ul style={{ listStyleType: 'none', padding: 0 }}>
        {kitaplar.map((kitap) => (
          <li key={kitap.id} style={{ 
            padding: '10px', 
            border: '1px solid #ddd', 
            marginBottom: '8px', 
            display: 'flex', 
            justifyContent: 'space-between', 
            alignItems: 'center',
            backgroundColor: kitap.okundu ? '#e2f0d9' : '#fff'
          }}>
            <div>
              <strong style={{ textDecoration: kitap.okundu ? 'line-through' : 'none' }}>{kitap.baslik}</strong> 
              <span style={{ fontSize: '0.9em', color: '#666' }}> - {kitap.yazar}</span>
            </div>
            <div style={{ display: 'flex', gap: '5px' }}>
              <button onClick={() => dispatch(durumDegistir(kitap.id))} style={{ cursor: 'pointer' }}>
                {kitap.okundu ? 'Okunmadı Yap' : 'Okundu Yap'}
              </button>
              <button onClick={() => 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çıyoruz
export default function App() {
  return (
    <Provider store={store}>
      <KitaplikUygulamasi />
    </Provider>
  );
}

2. Formik ve YUP ile TypeScript Form Doğrulamaları

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.

Uygulama Kod Mimarisi (LoginForm.tsx)

import React from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';

// 1. FORM VERİLERİNİN TYPESCRIPT TİPİNİ TANIMLIYORUZ
interface LoginValues {
  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.
const loginValidationSchema = 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()
});

export function LoginForm() {
  // 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.
  const formik = useFormik<LoginValues>({
    initialValues: {
      eposta: '',
      sifre: '',
      beniHatirla: false,
    },
    validationSchema: loginValidationSchema, // YUP şemasını buraya bağlıyoruz
    onSubmit: (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 (
    <div style={{ maxWidth: '400px', margin: '30px auto', padding: '20px', border: '1px solid #ccc', borderRadius: '5px' }}>
      <h3>Giriş Yap</h3>
      
      <form onSubmit={formik.handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
        
        {/* E-posta Alanı */}
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          <label htmlFor="eposta">E-posta</label>
          <input
            id="eposta"
            name="eposta"
            type="email"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur} // Kullanıcının inputtan çıkıp çıkmadığını izler
            value={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 ? (
            <span style={{ color: 'red', fontSize: '0.85em', marginTop: '4px' }}>{formik.errors.eposta}</span>
          ) : null}
        </div>

        {/* Şifre Alanı */}
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          <label htmlFor="sifre">Şifre</label>
          <input
            id="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 ? (
            <span style={{ color: 'red', fontSize: '0.85em', marginTop: '4px' }}>{formik.errors.sifre}</span>
          ) : null}
        </div>

        {/* Beni Hatırla Çentatısı (Checkbox) */}
        <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
          <input
            id="beniHatirla"
            name="beniHatirla"
            type="checkbox"
            onChange={formik.handleChange}
            checked={formik.values.beniHatirla}
          />
          <label htmlFor="beniHatirla">Beni Hatırla</label>
        </div>

        {/* Gönder Butonu */}
        <button 
          type="submit" 
          style={{ padding: '10px', backgroundColor: '#28a745', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
        >
          Giriş Yap
        </button>
      </form>
    </div>
  );
}

Neden Formik + YUP + TypeScript?

  1. 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.

  2. 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.

Modern Material UI + Formik + YUP Giriş Formu (MuiLoginForm.tsx)

import React from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';

// Material UI Bileşenlerini İçeri Aktarıyoruz
import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Checkbox from '@mui/material/Checkbox';
import FormControlLabel from '@mui/material/FormControlLabel';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import Paper from '@mui/material/Paper';

// 1. TYPESCRIPT TIP TANIMLAMASI
interface LoginValues {
  eposta: string;
  sifre: string;
  beniHatirla: boolean;
}

// 2. YUP GEÇERLİLİK ŞEMASI
const loginValidationSchema = 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()
});

export function MuiLoginForm() {
  // 3. FORMIK ENTEGRASYONU
  const formik = 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
    <Container component="main" maxWidth="xs" style={{ marginTop: '50px' }}>
      {/* Paper: Forma gölgeli, şık bir kart görünümü kazandırır */}
      <Paper elevation={3} style={{ padding: '30px', borderRadius: '8px' }}>
        
        {/* Typography: MUI'nin yazı ve başlık bileşenidir */}
        <Typography component="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ı */}
        <Box component="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.
          */}
          <TextField
            margin="normal"
            required
            fullWidth
            id="eposta"
            label="E-posta Adresi"
            name="eposta"
            autoComplete="email"
            autoFocus
            variant="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}
          />

          <TextField
            margin="normal"
            required
            fullWidth
            name="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 */}
          <FormControlLabel
            control={
              <Checkbox
                id="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 */}
          <Button
            type="submit"
            fullWidth
            variant="contained"
            color="primary"
            style={{ marginTop: '20px', padding: '10px 0', fontSize: '16px' }}
          >
            Giriş Yap
          </Button>

        </Box>
      </Paper>
    </Container>
  );
}

Kodun Sağladığı Mimari Kolaylıklar:

  1. 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.

  2. 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.

  3. 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.


📘 Kurs Tasarımı ve Konu Planı

Müfredat, bir yazılımcının bilmesi gereken en temel konulardan başlayarak ileri seviyeye doğru şu şekilde tasarlanmıştır:

  1. Giriş ve Çevre Kurulumu: Kod yazacağımız ortamı tanıma.

  2. Değişkenler ve Veri Tipleri: Bilgisayarın hafızasında bilgi saklama.

  3. Mantıksal Koşullar: Programın kararlar almasını sağlama.

  4. Döngüler (Loops): Tekrarlayan işleri otomatik yapma.

  5. Fonksiyonlar (Functions): Kodları paketleme ve tekrar kullanma.


🚀 "Öğrenecekleriniz" Paragrafının Açıklaması

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.


📖 Tüm Konuların Detaylı Açıklamaları, Örnekleri ve Uygulamaları

1. Giriş ve Çevre Kurulumu

Detaylı Açıklama

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.

💻 Örnek

Ekrana yazı yazdırmak için dünyadaki tüm yazılımcılar ilk önce "Merhaba Dünya" yazar.

print("Merhaba Dünya!")

🛠️ Uygulama: Kendini Tanıtan Program

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!")

2. Değişkenler ve Veri Tipleri

Detaylı Açıklama

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.

💻 Örnek

oyuncak_kutusu = "Kırmızı Araba"
yas = 12
boy = 1.45

🛠️ Uygulama: Süper Kahraman Kartı Oluşturma

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 = 300
ucabilir_mi = True

print("Kahramanın Adı:", kahraman_adi)
print("Özel Gücü:", gucu)
print("Hızı (km/s):", hizi)

3. Mantıksal Koşullar (If / Else)

Detaylı Açıklama

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.

💻 Örnek

hava_yagmurlu = True

if hava_yagmurlu == True:
    print("Şemsiyeni yanına al!")
else:
    print("Güneş gözlüğünü takabilirsin.")

🛠️ Uygulama: Lunapark Treni Giriş Kontrolü

Görev: Boyu 140 santimetreden kısa olanların hız trenine binmesini engelleyen bir sistem yazın.

boy_olcusu = 135

if boy_olcusu >= 140:
    print("Hız trenine binebilirsiniz! İyi eğlenceler. 🎢")
else:
    print("Üzgünüm, boyunuz tren için biraz kısa. 🛑")

4. Döngüler (Loops)

Detaylı Açıklama

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.

💻 Örnek

# Ekrana 5 defa Merhaba yazar
for sayi in range(5):
    print("Merhaba!")

🛠️ Uygulama: Roket Geri Sayım Sistemi

Görev: Roket fırlatılışı için 5'ten geriye doğru sayan ve sonunda "Fırlat!" diyen bir program yapın.

for sayi in [5, 4, 3, 2, 1]:
    print(sayi, "...")

print("🚀 Roket Fırlatıldı! 🚀")

5. Fonksiyonlar (Functions)

Detaylı Açıklama

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.

💻 Örnek

def sabah_selami():
    print("Günaydın! Harika bir gün dilerim.")

# Fonksiyonu çağırma
sabah_selami()

🛠️ Uygulama: Alan Hesaplama Sihirbazı

Görev: Kenar uzunlukları verilen bir dikdörtgenin alanını otomatik hesaplayan bir fonksiyon yazın.

def alan_hesapla(kisa_kenar, uzun_kenar):
    sonuc = kisa_kenar * uzun_kenar
    print("Bu bahçenin alanı:", sonuc, "metrekaredir.")

# Sihirbazı farklı sayılarla kullanalım
alan_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.


📘 2. Bölüm: İleri Seviye Konu Tasarımı ve Planı

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:

  1. Gelişmiş Veri Yapıları (Listeler ve Sözlükler): Birden fazla veriyi düzenli saklama.

  2. Nesne Tabanlı Programlama (OOP - Object Oriented Programming): Kodları gerçek dünya nesneleri gibi tasarlama.

  3. Hata Yönetimi (Try / Except): Programın çömesini engelleme ve hataları yakalama.

  4. Dosya İşlemleri (File Handling): Verileri bilgisayara kalıcı olarak kaydetme ve okuma.


📖 İleri Seviye Konuların Detaylı Açıklamaları ve Çift Uygulamaları

1. Gelişmiş Veri Yapıları (Lists & Dictionaries)

Detaylı Açıklama

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.


🎮 Uygulama 1: Oyun Yapımı (RPG Oyunu Karakter Envanteri)

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ıyor
print(kahraman_ozellikleri["İsim"], "maceraya başlıyor...")
print("Çantadaki ilk eşya:", sirt_cantasi[0])

# Özellik güncelleme
kahraman_ozellikleri["Can"] = 100  # Canı fulledik
kahraman_ozellikleri["Altın"] += 50  # 50 altın kazandı

print("Güncel Altın Miktarı:", kahraman_ozellikleri["Altın"])

📐 Uygulama 2: Matematiksel Hesaplamalar (Sınıf Not Ortalaması ve İstatistik)

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ı saklama
matematik_notlari = [85, 90, 78, 60, 100, 92, 45]

# Notların toplamını bulma
toplam_not = sum(matematik_notlari)
ogrenci_sayisi = len(matematik_notlari)

# Ortalama hesaplama
sinif_ortalamasi = toplam_not / ogrenci_sayisi

print("Sınıftaki Toplam Öğrenci Sayısı:", ogrenci_sayisi)
print("Sınıfın Matematik Not Ortalaması:", round(sinif_ortalamasi, 2))

2. Nesne Tabanlı Programlama (OOP)

Detaylı Açıklama

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.


🎮 Uygulama 1: Oyun Yapımı (Düşman Canavar Sınıfı)

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.

class Canavar:
    def __init__(self, ad, can, hasar_gucu):
        self.ad = ad
        self.can = can
        self.hasar_gucu = hasar_gucu

    def saldir(self, hedef_canavar):
        print(f"⚔️ {self.ad}, {hedef_canavar.ad} adlı canavara saldırdı!")
        hedef_canavar.can -= self.hasar_gucu
        print(f"🛑 {hedef_canavar.ad} canavarının kalan canı: {hedef_canavar.can}")

# Canavar kalıbından iki adet nesne üretiyoruz
ork = Canavar("Vahşi Ork", 80, 15)
zombi = Canavar("Zombi", 50, 10)

# Ork, Zombi'ye saldırıyor
ork.saldir(zombi)

📐 Uygulama 2: Matematiksel Hesaplamalar (Geometrik Şekil Kalıbı)

Görev: Bir dairenin alanını ve çevresini nesne yönelimli olarak hesaplayan bir daire sınıfı tasarlayın.

class Daire:
    def __init__(self, yaricap):
        self.yaricap = yaricap
        self.pi = 3.14159

    def alan_hesapla(self):
        return self.pi * (self.yaricap ** 2)

    def cevre_hesapla(self):
        return 2 * self.pi * self.yaricap

# 5 cm yarıçapında bir daire nesnesi oluşturalım
kucuk_daire = Daire(5)

print("Dairenin Alanı:", kucuk_daire.alan_hesapla())
print("Dairenin Çevresi:", kucuk_daire.cevre_hesapla())

3. Hata Yönetimi (Try / Except)

Detaylı Açıklama

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.


🎮 Uygulama 1: Oyun Yapımı (Kullanıcı Girişi Doğrulama)

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...")
except ValueError:
    print("⚠️ HATA: Lütfen sadece sayısal bir değer girin! Harf girilemez.")
except IndexError:
    print("⚠️ HATA: Sadece 1, 2 veya 3 numaralı karakteri seçebilirsiniz.")

📐 Uygulama 2: Matematiksel Hesaplamalar (Sıfıra Bölme Hatası Önleyici)

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.

def bolme_islemi(sayi1, sayi2):
    try:
        sonuc = sayi1 / sayi2
        return sonuc
    except ZeroDivisionError:
        return "⚠️ HATA: Bir sayı 0'a bölünemez! Matematiksel kurallara aykırı."

# Test edelim
print("Doğru İşlem (10 / 2):", bolme_islemi(10, 2))
print("Hatalı İşlem (10 / 0):", bolme_islemi(10, 0))

4. Dosya İşlemleri (File Handling)

Detaylı Açıklama

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.


🎮 Uygulama 1: Oyun Yapımı (En Yüksek Skoru Kaydetme)

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 fonksiyonu
def skoru_kaydet(skor):
    with open("high_score.txt", "w") as dosya:
        dosya.write(str(skor))
    print("En yüksek skor başarıyla kaydedildi!")

# Skoru dosyadan okuma fonksiyonu
def skoru_oku():
    try:
        with open("high_score.txt", "r") as dosya:
            icerik = dosya.read()
            return int(icerik)
    except FileNotFoundError:
        # Eğer dosya henüz hiç oluşturulmadıysa ilk skor 0'dır
        return 0

# Oyun açılıyor: Eski skoru oku
eski_rekor = skoru_oku()
print("Oyuna hoş geldiniz! Şu anki rekorunuz:", eski_rekor)

# Oyuncu yeni bir rekor kırdı diyelim
yeni_skor = 1250
if yeni_skor > eski_rekor:
    print("🎉 TEBRİKLER! Yeni rekor kırdınız!")
    skoru_kaydet(yeni_skor)

📐 Uygulama 2: Matematiksel Hesaplamalar (Faktöriyel Sonuç Log Kaydı)

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.

def faktoriyel_hesapla_ve_kaydet(sayi):
    # Faktöriyel hesaplama mantığı
    faktoriyel = 1
    for i in range(1, sayi + 1):
        faktoriyel *= i
    
    # Sonucu dosyaya ekleme ( 'a' modu ile eskisini silmeden altına yazar)
    with open("matematik_raporu.txt", "a", encoding="utf-8") as rapor:
        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önderelim
faktoriyel_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.


🎮 1. Proje: Metin Tabanlı Macera Oyunu (Karanlık Mağara)

Bu oyun; fonksiyonları, sözlükleri (karakter özellikleri için), listeleri (envanter sistemi için) ve döngüleri aktif olarak kullanır.

import random

# Oyunun ana verileri (Sözlük ve Listeler)
kahraman = {
    "İsim": "",
    "Can": 100,
    "Altın": 0,
    "Saldırı Gücü": 15
}
envanter = ["Tahta Kalkan"]

def oyun_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()

def durum_goster():
    print(f"\n--- {kahraman['İsim']} Durum Raporu ---")
    print(f"Can: {kahraman['Can']} | Altın: {kahraman['Altın']} | Çanta: {envanter}")
    print("-----------------------------------")

def magara_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): ")
    
    if secim == "1":
        aydinlik_tunel()
    elif secim == "2":
        karanlik_tunel()
    else:
        print("Yanlış seçim yaptın, panikten mağara tavanı çöktü!")
        kahraman["Can"] -= 20
        magara_girişi()

def aydinlik_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ü"] += 10
    
    print("\nTünelin sonu büyük bir kapıya çıkıyor. Kapıyı açıyorsun...")
    canavar_odasi()

def karanlik_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()
    if secim == "evet":
        altin_miktari = random.randint(30, 80)
        print(f"🎉 Harika! Sandıktan {altin_miktari} altın çıktı!")
        kahraman["Altın"] += altin_miktari
    else:
        print("Tedbirli davrandın ve sandığa dokunmadın.")
        
    print("\nGizli geçit seni büyük bir odaya çıkarıyor...")
    canavar_odasi()

def canavar_odasi():
    durum_goster()
    print("\n🛑 EYVAH! Karşına Mağara Trolü çıktı!")
    trol_can = 50
    
    while trol_can > 0 and kahraman["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?: ")
        
        if secim == "1":
            print(f"⚔️ Trolün üzerine atıldın ve {kahraman['Saldırı Gücü']} hasar verdin!")
            trol_can -= kahraman["Saldırı Gücü"]
            
            if trol_can > 0:
                trol_hasar = random.randint(10, 25)
                if "Tahta Kalkan" in envanter:
                    trol_hasar -= 5
                    print(f"🛡️ Kalkanın sayesinde trolün darbesini hafiflettin!")
                print(f"💥 Trol sana vurdu ve {trol_hasar} canın gitti!")
                kahraman["Can"] -= trol_hasar
        elif secim == "2":
            if random.random() > 0.5:
                print("🏃‍♂️ Muhteşem bir hızla trolden kaçmayı başardın ve mağaradan dışarı çıktın!")
                oyun_kazan()
                return
            else:
                print("🛑 Kaçmaya çalışırken trol seni yakaladı ve sırtına vurdu!")
                kahraman["Can"] -= 20
        else:
            print("Kararsız kaldın, trol boş durmadı!")
            kahraman["Can"] -= 15

    if kahraman["Can"] <= 0:
        oyun_kayip()
    else:
        print("\n🏆 Trolü yendin! Mağaranın çıkış kapısı açıldı!")
        kahraman["Altın"] += 100
        oyun_kazan()

def oyun_kazan():
    print("\n=============================================")
    print(f"TEBRİKLER! Oyunu kazandın, {kahraman['İsim']}!")
    print(f"Toplam Toplanan Altın: {kahraman['Altın']}")
    print("=============================================")

def oyun_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()

📐 2. Proje: Gelişmiş İstatistik ve Hesap Makinesi

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.

def veri_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")
    
    while True:
        girdi = input("Bir sayı girin: ").strip().lower()
        if girdi == "bitti":
            break
        try:
            sayi = float(girdi)
            sayilar.append(sayi)
        except ValueError:
            print("⚠️ Hata: Lütfen geçerli bir sayı girin ya da 'bitti' yazın.")
            
    return sayilar

def ortalama_hesapla(liste):
    if len(liste) == 0:
        return 0
    return sum(liste) / len(liste)

def medyan_hesapla(liste):
    if len(liste) == 0:
        return 0
    sirali_liste = sorted(liste)
    n = len(sirali_liste)
    orta = n // 2
    
    if n % 2 == 1:
        # Eleman sayısı tek ise ortadaki sayı
        return sirali_liste[orta]
    else:
        # Eleman sayısı çift ise ortadaki iki sayının ortalaması
        return (sirali_liste[orta - 1] + sirali_liste[orta]) / 2

def standart_sapma_hesapla(liste, ortalama):
    if len(liste) <= 1:
        return 0
    toplam_kare_fark = 0
    for sayi in input_listesi:
        toplam_kare_fark += (sayi - ortalama) ** 2
    
    # Varyans ve ardından karekök (Standart Sapma)
    varyans = toplam_kare_fark / (len(liste) - 1)
    return varyans ** 0.5

def rapor_ekrani(liste):
    if not liste:
        print("🛑 Hiç veri girilmediği için hesaplama yapılamadı.")
        return

    # Hesaplamaları yapalım
    eleman_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ım
    print("\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ım
input_listesi = veri_girisi_al()
rapor_ekrani(input_listesi)

📝 3. Mini İleri Seviye Bilgi Testi (Quiz)

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.

❓ Soru 1: Listeler ve Sözlükler

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?


❓ Soru 2: Nesne Tabanlı Programlama (OOP)

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?


❓ Soru 3: Hata Yönetimi (Try / Except)

try:
    print(10 / 0)
except ValueError:
    print("Geçersiz değer hatası!")
except ZeroDivisionError:
    print("Sıfıra bölme hatası!")

Yukarıdaki kod ekrana hangi mesajı yazar?


❓ Soru 4: Dosya İşlemleri (Modlar)

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?


🔑 Test Cevap Anahtarı ve Açıklamaları

  1. Doğru Cevap: B

  2. Doğru Cevap: C

  3. Doğru Cevap: B

  4. Doğru Cevap: C




***************************************************

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.


"Öğrenecekleriniz" Bölümünün Açıklaması

Bu bölüm, kursu bitirdiğinizde kazanacağınız becerileri ve hedefleri anlatır:


1. BÖLÜM: REACT TEMELLERİ (REACT FUNDAMENTALS)

React'e İlk Bakış

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.

React İçin Gerekli JavaScript Özetleri

React yazabilmek için modern JavaScript kurallarını çok iyi bilmek gerekir.

Bileşenler (Components), Props ve JSX ile Çalışmak

React uygulamaları yapboz parçalarından oluşur. Bu parçaların her birine Bileşen (Component) denir.

State (Durum), Olaylar (Events) ve Formlar

Bileşenlerin kendi içlerinde değişebilen bilgileri tutmasına State (Durum) denir. useState kancası (hook) ile oluşturulur.

React'te Düşünmek: State Yönetimi

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).

Uygulama Projesi: Eat-'N-Split (Hesap Bölüşme)


2. BÖLÜM: ORTA SEVİYE REACT (INTERMEDIATE REACT)

Bileşen Kompozisyonu ve Yeniden Kullanılabilirlik

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.

React Arka Planda Nasıl Çalışır?

React'in ekranı nasıl güncellediğini anlamak çok önemlidir.

Efektler ve Veri Çekme (Effects & Data Fetching)

React bileşenlerinin dış dünya ile iletişim kurmasını (örneğin internetten veri indirmek) sağlayan yapı useEffect hook'udur.

Özel Kancalar (Custom Hooks) ve Refs


3. BÖLÜM: İLERİ SEVİYE REACT + REDUX (ADVANCED REACT + REDUX)

Gelişmiş useReducer Hook'u

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.

React Router: Tek Sayfa Uygulamaları (SPA)

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.

Gelişmiş Durum Yönetimi: Context API

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.

Performans Optimizasyonu

Gereksiz ekran çizimlerini (Wasted Renders) engellemek için kullanılan yöntemlerdir.

Redux ve Modern Redux Toolkit (RTK)

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.


4. BÖLÜM: PROFESYONEL REACT GELİŞTİRME (PROFESSIONAL REACT DEVELOPMENT)

Veri Yüklemeli React Router (v6.4+)

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.

Tailwind CSS Kursu

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.

En Büyük Proje Başlangıcı ve Styled Components

Supabase ile Arka Plan (Back-End) Kurulumu

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.

React Query (TanStack Query): Uzaktan Durum Yönetimi

İ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.

Gelişmiş React Kalıpları (Advanced Patterns)

Bileşenleri üst seviye esnek yapmak için kullanılan profesyonel yazılım mimarileridir.

Ek Özellikler ve Canlıya Alma (Deployment)


5. BÖLÜM: NEXT.JS İLE FULL-STACK REACT (FULL-STACK REACT WITH NEXT.JS)

Next.js ve App Router Temelleri

Next.js, React'in üzerine kurulu, sitelerin Google'da üst sıralarda çıkmasını sağlayan (SEO dostu) güçlü bir framework'tür.

Uygulama Projesi: The Wild Oasis Müşteri Web Sitesi

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.

Veri Getirme, Önbelleğe Alma (Caching) ve Suspense

Sunucu Eylemleri (Server Actions) ve Kimlik Doğrulama

Vercel ile Canlıya Gönderim ve Eski Pages Router Özeti

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!


1. BÖLÜM: REACT TEMELLERİ (REACT FUNDAMENTALS)

1. JavaScript Özet Örnekleri (Essential JS for React)

React yazarken en çok kullanacağımız modern JavaScript (ES6+) özelliklerinin kod yapıları şunlardır:

Destructuring, Arrow Functions ve Nesne Metotları

// 1. Nesne Parçalara Ayırma (Object Destructuring)
const kullanıcı = { isim: "Ahmet", yas: 26, sehir: "Isparta" };
const { isim, yas } = kullanıcı; 
console.log(isim); // Çıktı: Ahmet

// 2. Ok Fonksiyonları (Arrow Functions)
const topla = (a, b) => a + b;
console.log(topla(5, 10)); // Çıktı: 15

// 3. Kısa Devre Değerlendirmesi (Short-Circuiting)
const inputKullaniciAdi = "";
const gecerliIsim = inputKullaniciAdi || "Misafir Kullanıcı"; 
console.log(gecerliIsim); // Çıktı: Misafir Kullanıcı

// 4. Diziyi Dönüştürme (Array Map)
const sayilar =;
const karesi = sayilar.map(sayi => sayi * sayi);
console.log(karesi); // Çıktı: [1, 4, 9]

2. Bileşenler, Props ve JSX ile Çalışmak

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.

Uygulama: Profil Kartı (v1)

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ır
function 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>
  );
}

3. State (Durum), Olaylar (Events) ve Formlar

Bileşen içindeki verilerin değişmesi ve ekranın güncellenmesi için useState kancasını (hook) kullanırız.

Uygulama: Adım Sayıcı (Steps Component) ve Form Kontrolü

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>
          <input 
            type="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>
  );
}

4. React'te Düşünmek: State Yönetimi ve Üste Taşıma (Lifting State Up)

Eğer bir formdan eklenen veri, başka bir bileşendeki listede gösterilecekse state'i ikisinin ortak babasına taşırız.

Uygulama: Seyahat Listesi (Far Away Travel List)

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ŞEN
export default function SeyahatUygulamasi() {
  // State babada duruyor, böylece hem forma hem listeye dağıtabiliyoruz
  const [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 fonksiyonu
  function 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önderiyoruz
    setTanim(""); // Input'u temizle
  }

  return (
    <form onSubmit={handleSubmit} style={{ margin: "20px 0" }}>
      <input 
        type="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>
  );
}

5. Pratik Proje: Eat-'N-Split (Hesap Bölüşme Uygulaması)

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ı bulma
    const 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.


2. BÖLÜM: ORTA SEVİYE REACT (INTERMEDIATE REACT)

1. Bileşen Kompozisyonu (Component Composition) ve 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.

Uygulama: Yıldız Derecelendirme (Star Rating Component)

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 (
    <span
      style={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 (
            <Yildiz
              key={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>
  );
}

2. Efektler ve İnternetten Veri Çekme (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.

Uygulama: usePopcorn (Canlı Film Arama ve Listeleme Yapısı)

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 seti
        const 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>
      <input
        type="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>
  );
}

3. 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.

Kod Örneği: Arama Kutusuna Otomatik Odaklanma ve Değer Tutma

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>
  );
}

4. Özel Kancalar (Custom Hooks) Yazmak

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.

Uygulama: 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 TANIMI
export function useLocalStorageState(baslangicDegeri, anahtarKelime) {
  // Lazy Initialization: İlk değeri hafızadan okuyarak başlatıyoruz
  const [deger, setDeger] = useState(() => {
    const kaydedilenVeri = localStorage.getItem(anahtarKelime);
    return kaydedilenVeri ? JSON.parse(kaydedilenVeri) : baslangicDegeri;
  });

  // Değer her değiştiğinde local storage alanını güncelle
  useEffect(() => {
    localStorage.setItem(anahtarKelime, JSON.stringify(deger));
  }, [deger, anahtarKelime]);

  return [deger, setDeger];
}

Bu Özel Kancanın Projede Kullanımı:

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ıyoruz

export 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>
  );
}

5. Klasik (Class-Based) React Örneği (Optional)

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ımlama
  constructor(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 fonksiyon
  dereceArtir = () => {
    this.setState({ derece: this.state.derece + 1 });
  };

  // 4. HTML/JSX çıktısını veren mecburi metot
  render() {
    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.


3. BÖLÜM: İLERİ SEVİYE REACT + REDUX (ADVANCED REACT + REDUX)

1. Gelişmiş 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.

Uygulama: Banka Hesabı Simülasyonu

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 yapma
  if (!state.aktif && action.type !== "hesapAc") return state;

  switch (action.type) {
    case "hesapAc":
      return { ...state, aktif: true, bakiye: 500 }; // Hoş geldin bonusu
    case "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>
  );
}

2. React Router ile Sayfa Yönlendirmeleri (SPA)

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.

Kod Mimari Yapısı: Sayfa Rotalarının Kurulumu

(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 Sayfa
function 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övdesi
export 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>
  );
}

3. Küresel Durum Yönetimi: Context API

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.

Uygulama: Karanlık/Aydınlık Tema Değiştirici (Theme Context)

import React, { createContext, useState, useContext } from "react";

// 1. Context Deposunu Oluşturuyoruz
const TemaContext = createContext();

// 2. Sağlayıcı (Provider) Bileşeni
export 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ıyoruz
  return (
    <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ıyoruz
  const { 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şi
export 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>
  );
}

4. Redux ve Modern Redux Toolkit (RTK)

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.

Uygulama: Sayaç Yönetimi (Counter Slice Yapısı)

(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ıyoruz
const sayacDilimi = 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ıyoruz
export const { artir, azalt, miktarlaArtir } = sayacDilimi.actions;

// 2. Merkezi Mağaza (Store) Kurulumu
export const store = configureStore({
  reducer: {
    sayacDeposu: sayacDilimi.reducer
  }
});

Redux Mağazasının React Bileşenine Bağlanması:

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { artir, azalt, miktarlaArtir } from "./sayacDilimi"; // Aksiyonları alıyoruz

export function ReduxSayacBileseni() {
  // useSelector ile merkezi depodaki istediğimiz veriyi çekiyoruz
  const 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.


4. BÖLÜM: PROFESYONEL REACT GELİŞTİRME (PROFESSIONAL REACT DEVELOPMENT)

1. Veri Yüklemeli React Router (v6.4+ Loaders & Actions)

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.

Kod Mimari Yapısı

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ırlar
export 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şeni
function MenuSayfasi() {
  // useLoaderData kancası yardımıyla yukarıdaki loader fonksiyonunun getirdiği veriyi okuyoruz
  const 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ştirme
const 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} />;
}

2. React Query (TanStack Query): Uzaktan Durum Yönetimi

İ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.

Uygulama: Otel Odaları Listesi ve Silme İşlemi (Supabase Bağlantılı Sahte Düzen)

(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) kaydeder
  const { data: odalar, isLoading, isError } = useQuery({
    queryKey: ["odalar"], // Hafızadaki benzersiz anahtar kelime
    queryFn: odalariGetir // Veriyi çeken fonksiyon
  });

  // 2. VERİ DEĞİŞTİRME (Mutation): Silme, ekleme veya güncelleme işlemleri için kullanılır
  const { 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)
            <button 
              onClick={() => odaSil(oda.id)} 
              disabled={siliniyor}
              style={{ marginLeft: "15px", color: "white", background: "red", border: "none", padding: "5px 10px", cursor: "pointer" }}
            >
              Sil
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

3. Gelişmiş React Tasarım Kalıpları: Compound Components (Birleşik Bileşenler)

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.

Uygulama: Özel Açılır Menü (Açılır/Kapanır Kart Yapısı)

import React, { createContext, useState, useContext } from "react";

// 1. Ortak bir Context iletişimi kuruyoruz
const 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 çizme
  return <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.


5. BÖLÜM: NEXT.JS İLE FULL-STACK REACT (FULL-STACK REACT WITH NEXT.JS)

1. React Server Components (RSC) ile Doğrudan Veri Çekme

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.

Kod Mimari Yapısı (Next.js Sunucu Dosyası)

// 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>
  );
}

2. React Suspense ile Parçalı Yükleme (Streaming UI)

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.

Uygulama Yapısı

import React, { Suspense } from "react";
import YavasYüklenenKabinListesi from "./KabinListesi"; // Ağır bir veri çekme işlemi içeriyor

function 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>
  );
}

3. Server Actions (Sunucu Eylemleri) ve Modern Kancalar

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.

Uygulama: Profil Bilgisi Güncelleme Formu

// src/app/profil/actions.js
"use server"; // Bu dosyadaki tüm fonksiyonların sadece sunucuda çalışacağını garanti eder

// Sunucu üzerinde çalışacak fonksiyon
export 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"); 
}

Bu Sunucu Eyleminin Form İçinde Kullanılması:

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ıyoruz

import React, { useOptimistic } from "react";
import { profilGuncelleAction } from "./actions"; // Sunucu fonksiyonunu çağırıyoruz

export 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 yaz
    await 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" }}>
        <input 
          type="text" 
          name="kullaniciIsim" 
          placeholder="Yeni adınızı yazın..." 
          required 
          style={{ padding: "8px" }}
        />
        <input 
          type="email" 
          name="kullaniciEposta" 
          placeholder="E-posta adresiniz..." 
          required 
          style={{ 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.


1. MODÜL: MEVCUT UYGULAMALARIN DERİNLEMESİNE GENİŞLETİLMESİ

Önceki adımlarda yazdığımız temel uygulamalara kurumsal seviyede yeni özellikler, performans optimizasyonları ve hata yönetimleri ekliyoruz.

A. Seyahat Bagaj Listesi (1. Bölüm) -> "Gelişmiş Kategorizasyon, Sıralama ve Filtreleme"

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, unpacked
  const [sirala, setSirala] = useState("input"); // input, description, packedStatus

  function 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ĞI
  let 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>
  );
}

B. usePopcorn (2. Bölüm) -> "Sonsuz Kaydırma Simülasyonu & Hata Sınırları"

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 mimari
  useEffect(() => {
    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>
  );
}

2. MODÜL: MÜLAKAT SORULARI VE PRATİK ÇÖZÜMLERİ

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ı:

Soru 1: 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ılar
  return () => {
    aktif = false;
  };
}, [arama]);

Soru 2: useMemo ve useCallback tam olarak ne zaman kullanılmalıdır? Gereksiz kullanımların zararı nedir?

Cevap:

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.

Soru 3: React'teki "Prop Drilling" problemi Context API dışında mimari olarak nasıl çözülebilir?

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>
  );
}

3. MODÜL: SIFIRDAN YENİ BİR PROJENİN KLASÖR YAPISI VE MİMARİ PLANI

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:

Standart Proje Klasör Ağacı (Directory Structure)

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

Projenin Mimari Tasarım Planı (Architectural Guidelines)

  1. 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.

  2. 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.

  3. 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.


*************************************************************************************************************************