React, kullanıcı arayüzleri oluşturmak için en popüler JavaScript kütüphanesidir. Bu rehberde modern React ekosistemini kullanarak sıfırdan üretim kalitesinde bir web uygulaması geliştireceğiz. Vite, React Router, Zustand ve TanStack Query gibi güncel araçlarla gerçek dünya projelerinde kullanabileceğiniz bir temel oluşturacağız.
Proje Başlatma: Vite vs Create React App
Create React App (CRA) artık resmi olarak önerilmiyor. Vite, ESBuild tabanlı derleme sistemiyle çok daha hızlı geliştirme deneyimi sunar. Hot Module Replacement (HMR) milisaniyeler içinde gerçekleşir.
# Vite ile React + TypeScript projesi oluştur
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev # http://localhost:5173 açılır
my-app/
├── public/
│ └── vite.svg
├── src/
│ ├── assets/
│ ├── components/
│ │ ├── ui/ # Genel UI bileşenleri
│ │ └── layout/ # Header, Footer, Sidebar
│ ├── pages/ # Sayfa bileşenleri
│ ├── hooks/ # Özel hook'lar
│ ├── services/ # API çağrıları
│ ├── store/ # State management
│ ├── types/ # TypeScript tipleri
│ ├── utils/ # Yardımcı fonksiyonlar
│ ├── App.tsx
│ └── main.tsx
├── .env
├── vite.config.ts
└── package.json
Component Mimarisi
React'te her şey bileşendir (component). İyi bir bileşen yapısı; tek sorumluluk ilkesine uyar, yeniden kullanılabilir, ve test edilebilir olmalıdır. Props ile veri alır, kendi iç state'ini yönetir.
// src/components/ui/Button.tsx
import { ButtonHTMLAttributes, ReactNode } from 'react';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
children: ReactNode;
}
export function Button({
variant = 'primary',
size = 'md',
loading = false,
children,
className = '',
disabled,
...props
}: ButtonProps) {
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors';
const variantClasses = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
danger: 'bg-red-600 text-white hover:bg-red-700'
};
const sizeClasses = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
};
return (
<button
className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}
disabled={disabled || loading}
{...props}
>
{loading && <span className="animate-spin mr-2">⟳</span>}
{children}
</button>
);
}
React Hooks Derinlemesine
Hook'lar, fonksiyonel bileşenlerde state, side effect ve context gibi React özelliklerini kullanmanızı sağlar. En çok kullanılan hook'lar useState, useEffect, useMemo, useCallback ve useRef'tir.
Özel Hook Yazma
Tekrarlayan mantığı özel hook'lara taşıyarak kodunuzu DRY (Don't Repeat Yourself) tutabilirsiniz. Aşağıda API çağrıları için kullanışlı bir useFetch hook'u:
// src/hooks/useFetch.ts
import { useState, useEffect } from 'react';
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: string | null;
refetch: () => void;
}
export function useFetch<T>(url: string): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [trigger, setTrigger] = useState(0);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
fetch(url, { signal: controller.signal })
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then(json => setData(json))
.catch(err => {
if (err.name !== 'AbortError') setError(err.message);
})
.finally(() => setLoading(false));
return () => controller.abort();
}, [url, trigger]);
const refetch = () => setTrigger(t => t + 1);
return { data, loading, error, refetch };
}
State Management: Zustand
Global state yönetimi için Zustand, Redux'a kıyasla çok daha az boilerplate gerektirir. Küçük ve orta ölçekli projelerde ideal bir seçimdir. TypeScript desteği yerleşiktir.
npm install zustand
// src/store/authStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface User {
id: number;
name: string;
email: string;
}
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
login: (user: User, token: string) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
token: null,
isAuthenticated: false,
login: (user, token) => set({
user,
token,
isAuthenticated: true
}),
logout: () => set({
user: null,
token: null,
isAuthenticated: false
})
}),
{ name: 'auth-storage' } // localStorage'a kaydeder
)
);
// Kullanım:
// const { user, login, logout } = useAuthStore();
// login({ id: 1, name: 'Ali', email: 'ali@test.com' }, 'jwt-token');
API Entegrasyonu
API çağrılarını merkezi bir servis katmanında toplamak, bakım kolaylığı sağlar. Axios veya native fetch ile bir API istemcisi oluşturup, tüm endpointleri buradan yönetebilirsiniz.
// src/services/api.ts
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api';
async function request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = localStorage.getItem('auth-storage');
const parsed = token ? JSON.parse(token) : null;
const res = await fetch(`${API_URL}${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
...(parsed?.state?.token && {
Authorization: `Bearer ${parsed.state.token}`
}),
...options.headers
}
});
if (!res.ok) {
const error = await res.json().catch(() => ({ error: 'Sunucu hatası' }));
throw new Error(error.error || `HTTP ${res.status}`);
}
return res.json();
}
// API endpoint'leri
export const api = {
auth: {
login: (data: { email: string; password: string }) =>
request('/auth/login', { method: 'POST', body: JSON.stringify(data) }),
register: (data: { name: string; email: string; password: string }) =>
request('/auth/register', { method: 'POST', body: JSON.stringify(data) })
},
users: {
list: (page = 1) => request(`/users?page=${page}`),
get: (id: number) => request(`/users/${id}`),
update: (id: number, data: any) =>
request(`/users/${id}`, { method: 'PUT', body: JSON.stringify(data) })
}
};
React Router ile Sayfa Yönlendirme
npm install react-router-dom
// src/App.tsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useAuthStore } from './store/authStore';
import Layout from './components/layout/Layout';
import Home from './pages/Home';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import NotFound from './pages/NotFound';
// Korumalı rota bileşeni
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore(s => s.isAuthenticated);
return isAuthenticated ? <>{children}</> : <Navigate to="/login" replace />;
}
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</BrowserRouter>
);
}
Build ve Deploy
Vite, üretim build'ini Rollup ile oluşturur. Otomatik code splitting, tree shaking ve minification uygular. Çıktı dist/ klasörüne yazılır ve herhangi bir statik dosya sunucusunda host edilebilir.
# Üretim build
npm run build
# Build çıktısını test et
npm run preview # http://localhost:4173
# Build dosya boyutlarını analiz et
npx vite-bundle-visualizer
// vite.config.ts — üretim optimizasyonları
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
target: 'es2020',
minify: 'terser',
sourcemap: false,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom']
}
}
}
},
server: {
proxy: {
'/api': 'http://localhost:3000'
}
}
});
Performans İpuçları
- React.lazy() ve Suspense ile code splitting uygulayın — her sayfa ayrı chunk olarak yüklenir
- useMemo ve useCallback ile gereksiz render'ları önleyin
- Görselleri WebP/AVIF formatında ve lazy loading ile yükleyin
- Virtualization (react-window/tanstack-virtual) ile uzun listeleri optimize edin
- React DevTools Profiler ile render performansını analiz edin
- Bundle boyutunu izleyin — vendor chunk'ın 200KB altında kalmasını hedefleyin
// Lazy loading örneği
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<div className="p-8 text-center">Yükleniyor...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}