React Hooks Nedir?
React Hooks, function component'lerde state ve diğer React özelliklerini kullanmamızı sağlayan fonksiyonlardır. React 16.8'de tanıtılmıştır ve modern React geliştirmenin temelini oluşturur.
1. useState Hook
useState, function component'lerde state yönetimi için kullanılan temel hook'tur.
Temel Kullanım
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
</div>
);
}
Object State Kullanımı
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const updateUser = (field, value) => {
setUser(prevUser => ({
...prevUser,
[field]: value
}));
};
return (
<form>
<input
value={user.name}
onChange={(e) => updateUser('name', e.target.value)}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => updateUser('email', e.target.value)}
placeholder="Email"
/>
</form>
);
}
2. useEffect Hook
useEffect, side effect'leri yönetmek için kullanılır. componentDidMount, componentDidUpdate ve componentWillUnmount'un yerini alır.
Temel Kullanım
import React, { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Component mount olduğunda çalışır
fetchUsers();
}, []); // Empty dependency array
const fetchUsers = async () => {
try {
setLoading(true);
const response = await fetch('/api/users');
const userData = await response.json();
setUsers(userData);
} catch (error) {
console.error('Error fetching users:', error);
} finally {
setLoading(false);
}
};
if (loading) return <div>Loading...</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Dependency Array
function SearchResults({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
if (query) {
searchAPI(query).then(setResults);
}
}, [query]); // query değiştiğinde çalışır
return (
<div>
{results.map(result => (
<div key={result.id}>{result.title}</div>
))}
</div>
);
}
Cleanup Functions
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// Cleanup function
return () => clearInterval(interval);
}, []);
return <div>Seconds: {seconds}</div>;
}
3. useContext Hook
useContext, Context API'sini kullanarak component'ler arası veri paylaşımını sağlar.
import React, { createContext, useContext, useState } from 'react';
// Context oluştur
const AuthContext = createContext();
// Provider component
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (userData) => {
setUser(userData);
localStorage.setItem('user', JSON.stringify(userData));
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
};
const value = {
user,
login,
logout,
isAuthenticated: !!user
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
// Custom hook
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
// Kullanım
function LoginButton() {
const { user, login, logout, isAuthenticated } = useAuth();
if (isAuthenticated) {
return (
<div>
Welcome, {user.name}!
<button onClick={logout}>Logout</button>
</div>
);
}
return <button onClick={() => login({ name: 'John' })}>Login</button>;
}
4. useReducer Hook
Kompleks state logic için useReducer kullanılır. Redux pattern'ini component seviyesinde uygular.
import React, { useReducer } from 'react';
// Reducer function
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, {
id: Date.now(),
text: action.payload,
completed: false
}]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
};
default:
return state;
}
}
// Initial state
const initialState = {
todos: []
};
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, initialState);
const [inputValue, setInputValue] = useState('');
const addTodo = () => {
if (inputValue.trim()) {
dispatch({ type: 'ADD_TODO', payload: inputValue });
setInputValue('');
}
};
return (
<div>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
/>
<button onClick={addTodo}>Add Todo</button>
<ul>
{state.todos.map(todo => (
<li key={todo.id}>
<span
style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}
onClick={() => dispatch({
type: 'TOGGLE_TODO',
payload: todo.id
})}
>
{todo.text}
</span>
<button onClick={() => dispatch({
type: 'DELETE_TODO',
payload: todo.id
})}>
Delete
</button>
</li>
))}
</ul>
</div>
);
}
5. useMemo Hook
useMemo, expensive calculations'ı cache'lemek için kullanılır.
import React, { useMemo, useState } from 'react';
function ExpensiveComponent({ items, searchTerm }) {
// Bu hesaplama sadece items veya searchTerm değiştiğinde yapılır
const filteredItems = useMemo(() => {
console.log('Filtering items...'); // Debug için
return items.filter(item =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [items, searchTerm]);
const expensiveValue = useMemo(() => {
console.log('Calculating expensive value...');
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += i;
}
return result;
}, []); // Empty array - sadece bir kez hesaplanır
return (
<div>
<p>Expensive Value: {expensiveValue}</p>
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
6. useCallback Hook
useCallback, function'ları cache'lemek için kullanılır. Child component'lere geçirilen callback'leri optimize eder.
import React, { useState, useCallback, memo } from 'react';
// Child component - React.memo ile wrap edilmiş
const ChildComponent = memo(({ onButtonClick, name }) => {
console.log('ChildComponent rendered for:', name);
return (
<button onClick={onButtonClick}>
Click me ({name})
</button>
);
});
function ParentComponent() {
const [count, setCount] = useState(0);
const [otherState, setOtherState] = useState(0);
// Bu callback sadece count değiştiğinde yeniden oluşturulur
const handleButtonClick = useCallback(() => {
console.log('Button clicked, count:', count);
setCount(c => c + 1);
}, [count]);
// Bu callback hiç değişmez
const handleOtherClick = useCallback(() => {
setOtherState(s => s + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<p>Other State: {otherState}</p>
<ChildComponent
onButtonClick={handleButtonClick}
name="counter"
/>
<ChildComponent
onButtonClick={handleOtherClick}
name="other"
/>
</div>
);
}
7. Custom Hooks
Custom hook'lar, logic'i component'ler arasında paylaşmak için kullanılır.
useLocalStorage Hook
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// State'i localStorage'dan al veya initial value kullan
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
});
// State değişikliklerini localStorage'a yaz
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
};
return [storedValue, setValue];
}
// Kullanım
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [language, setLanguage] = useLocalStorage('language', 'en');
return (
<div>
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<select value={language} onChange={(e) => setLanguage(e.target.value)}>
<option value="en">English</option>
<option value="tr">Türkçe</option>
</select>
</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(() => {
let isCancelled = false;
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!isCancelled) {
setData(result);
}
} catch (err) {
if (!isCancelled) {
setError(err.message);
}
} finally {
if (!isCancelled) {
setLoading(false);
}
}
};
fetchData();
// Cleanup function
return () => {
isCancelled = true;
};
}, [url, JSON.stringify(options)]);
return { data, loading, error };
}
// Kullanım
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
8. Hook Rules (Kurallar)
React Hook'ları kullanırken uymanız gereken önemli kurallar:
1. Sadece Function Component'lerde veya Custom Hook'larda Kullanın
// ✅ Doğru - Function component içinde
function MyComponent() {
const [state, setState] = useState(0);
return <div>{state}</div>;
}
// ✅ Doğru - Custom hook içinde
function useCounter() {
const [count, setCount] = useState(0);
return { count, setCount };
}
// ❌ Yanlış - Class component içinde
class MyClassComponent extends React.Component {
constructor(props) {
super(props);
const [state, setState] = useState(0); // Hata!
}
}
2. Hook'ları En Üst Seviyede Çağırın
// ✅ Doğru
function MyComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// Logic buraya
return <div>...</div>;
}
// ❌ Yanlış - Koşullu hook kullanımı
function MyComponent() {
if (someCondition) {
const [count, setCount] = useState(0); // Hata!
}
// ❌ Yanlış - Loop içinde hook
for (let i = 0; i < 5; i++) {
useEffect(() => {}, []); // Hata!
}
return <div>...</div>;
}
9. Performance Optimization
useState Lazy Initial State
// ❌ Bu her render'da çalışır
const [state, setState] = useState(expensiveCalculation());
// ✅ Bu sadece ilk render'da çalışır
const [state, setState] = useState(() => expensiveCalculation());
useEffect Dependency Array
// ❌ Her render'da çalışır
useEffect(() => {
fetchData();
});
// ❌ Sadece mount/unmount'da çalışır ama dependency eksik
useEffect(() => {
fetchData(userId);
}, []);
// ✅ userId değiştiğinde çalışır
useEffect(() => {
fetchData(userId);
}, [userId]);
10. Best Practices
1. Hook'ları Gruplayın
function MyComponent() {
// State hooks
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState([]);
// Effect hooks
useEffect(() => {
// ...
}, []);
// Custom hooks
const { user } = useAuth();
const theme = useTheme();
// Component logic
// ...
}
2. Custom Hook'ları Kullanın
// Logic'i custom hook'a taşıyın
function useApiData(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// fetch logic
}, [url]);
return { data, loading, error };
}
3. Dependency Array'i Doğru Kullanın
// ESLint plugin kullanın: eslint-plugin-react-hooks
// Bu otomatik olarak eksik dependency'leri tespit eder
useEffect(() => {
// count ve name kullanılıyor
console.log(count, name);
}, [count, name]); // İkisini de dependency'e ekleyin
Sonuç
React Hooks modern React geliştirmenin temelidir. Bu rehberde öğrendiklerinizi özetlersek:
- useState: State yönetimi
- useEffect: Side effect'ler ve lifecycle
- useContext: Global state paylaşımı
- useReducer: Kompleks state logic
- useMemo/useCallback: Performance optimization
- Custom Hooks: Logic paylaşımı
Hook'ları öğrenmek React'te daha temiz, daha okunabilir ve daha maintainable kod yazmanızı sağlar. Practice yaparak bu kavramları pekiştirin!