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:

Hook'ları öğrenmek React'te daha temiz, daha okunabilir ve daha maintainable kod yazmanızı sağlar. Practice yaparak bu kavramları pekiştirin!