🚀 Giriş
React Hooks, React 16.8 ile birlikte gelen ve functional component'lerde state ve diğer React özelliklerini kullanmamızı sağlayan güçlü bir API. Bu rehberde, en önemli hook'ları detaylıca inceleyeceğiz ve gerçek dünya örnekleriyle nasıl kullanılacağını göreceğiz.
💡 Bu yazıda öğrenecekleriniz:
- useState ile state yönetimi
- useEffect ile lifecycle işlemleri
- useContext ile global state
- Custom hook'lar nasıl yazılır
- Hook'lar için best practice'ler
📊 useState Hook
useState
hook'u, functional component'lerde state kullanmamızı sağlar. En basit ve en sık kullanılan hook'lardan biridir.
Temel Kullanım
import React, { useState } from 'react';
function Counter() {
// State tanımlama
const [count, setCount] = useState(0);
return (
<div>
<p>Sayı: {count}</p>
<button onClick={() => setCount(count + 1)}>
Artır
</button>
<button onClick={() => setCount(count - 1)}>
Azalt
</button>
</div>
);
}
Fonksiyonel Update
State güncellemelerinde önceki değeri kullanırken, fonksiyonel update tercih edilmelidir:
// ❌ Önerilmeyen
setCount(count + 1);
// ✅ Önerilen
setCount(prevCount => prevCount + 1);
Object State Yönetimi
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const updateName = (newName) => {
setUser(prevUser => ({
...prevUser,
name: newName
}));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateName(e.target.value)}
placeholder="İsim"
/>
<p>Merhaba {user.name}!</p>
</div>
);
}
⚡ useEffect Hook
useEffect
hook'u, side effect'leri yönetmek için kullanılır. Component mount, update ve unmount lifecycle'larını yönetir.
Temel Kullanım
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Component mount olduğunda çalışır
fetchData();
}, []); // Boş dependency array
const fetchData = async () => {
try {
const response = await fetch('/api/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Veri yüklenirken hata:', error);
} finally {
setLoading(false);
}
};
if (loading) return <div>Yükleniyor...</div>;
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Cleanup Function
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
// Cleanup function
return () => {
clearInterval(interval);
};
}, []);
return <div>Geçen süre: {seconds} saniye</div>;
}
⚠️ Dikkat!
useEffect'te kullandığınız her değişkeni dependency array'e eklemeyi unutmayın.
ESLint kuralı exhaustive-deps
bu konuda yardımcı olacaktır.
🌐 useContext Hook
useContext
hook'u, Context API ile oluşturulan context'leri consume etmek için kullanılır. Prop drilling problemini çözer.
Context Oluşturma
// ThemeContext.js
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Custom hook
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
Context Kullanımı
// App.js
function App() {
return (
<ThemeProvider>
<Header />
<MainContent />
</ThemeProvider>
);
}
// Header.js
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={`header header--${theme}`}>
<h1>My App</h1>
<button onClick={toggleTheme}>
{theme === 'light' ? '🌙' : '☀️'}
</button>
</header>
);
}
🔧 Custom Hooks
Custom hook'lar, stateful logic'i component'ler arasında paylaşmak için kullanılır. Hook kurallarına uygun şekilde yazılmalıdır.
useLocalStorage Hook
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// State'i localStorage'dan initialize et
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error('localStorage okuma hatası:', error);
return initialValue;
}
});
// setValue fonksiyonu
const setValue = (value) => {
try {
// Functional update'i destekle
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error('localStorage yazma hatası:', error);
}
};
return [storedValue, setValue];
}
// Kullanım
function Settings() {
const [settings, setSettings] = useLocalStorage('appSettings', {
notifications: true,
theme: 'light'
});
return (
<div>
<label>
<input
type="checkbox"
checked={settings.notifications}
onChange={(e) => setSettings(prev => ({
...prev,
notifications: e.target.checked
}))}
/>
Bildirimler
</label>
</div>
);
}
useFetch Hook
import { useState, useEffect } from 'react';
function useFetch(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortController = new AbortController();
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url, {
...options,
signal: abortController.signal
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
setLoading(false);
}
};
fetchData();
// Cleanup: request'i iptal et
return () => abortController.abort();
}, [url]);
return { data, loading, error };
}
// Kullanım
function UserList() {
const { data: users, loading, error } = useFetch('/api/users');
if (loading) return <div>Yükleniyor...</div>;
if (error) return <div>Hata: {error}</div>;
return (
<ul>
{users?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
✅ Best Practices
1. Hook Kuralları
- Hook'ları sadece React function'ları içinde çağırın
- Hook'ları döngü, koşul veya nested function içinde çağırmayın
- Hook'ları her zaman component'in en üstünde kullanın
2. Performance Optimizasyonu
// useMemo ile expensive hesaplamaları cache'le
const expensiveValue = useMemo(() => {
return data.filter(item => item.active)
.map(item => ({ ...item, processed: true }));
}, [data]);
// useCallback ile function reference'ları stable tut
const handleClick = useCallback((id) => {
onItemClick(id);
}, [onItemClick]);
3. Error Handling
function useAsyncOperation() {
const [state, setState] = useState({
data: null,
loading: false,
error: null
});
const execute = useCallback(async (operation) => {
setState(prev => ({ ...prev, loading: true, error: null }));
try {
const result = await operation();
setState({ data: result, loading: false, error: null });
return result;
} catch (error) {
setState({ data: null, loading: false, error });
throw error;
}
}, []);
return { ...state, execute };
}
4. Testing
// Custom hook test
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
test('should increment counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
🎯 Sonuç
React Hooks, modern React development'in vazgeçilmez parçalarıdır. Bu rehberde gördüğümüz hook'lar ve best practice'ler ile daha temiz, daha maintainable ve daha performanslı React uygulamaları geliştirebilirsiniz.