React Hooks: En İyi Pratikler ve Performans İpuçları

Yunus Emre Güzel
16 Ocak 202510 dkReact
React Hooks: En İyi Pratikler ve Performans İpuçları

React Hooks, fonksiyonel bileşenlerde state yönetimi ve yaşam döngüsü özelliklerini kullanmamızı sağlayan güçlü bir özelliktir. Bu yazıda, React Hooks'un en iyi kullanım pratiklerini ve performans optimizasyonu için önemli ipuçlarını inceleyeceğiz.

useState Hook'u ve Re-render Optimizasyonu

useState hook'u, fonksiyonel bileşenlerde state yönetimi için en temel araçtır. Ancak yanlış kullanımı gereksiz re-render'lara yol açabilir.

// ❌ Yanlış Kullanım
const [user, setUser] = useState({
  name: 'John',
  age: 25,
  preferences: {
    theme: 'dark',
    notifications: true
  }
});

// Nested state güncellemesi
setUser({
  ...user,
  preferences: {
    ...user.preferences,
    theme: 'light'
  }
});

// ✅ Doğru Kullanım
const [user, setUser] = useState({
  name: 'John',
  age: 25
});
const [preferences, setPreferences] = useState({
  theme: 'dark',
  notifications: true
});

// State'leri ayrı ayrı yönetmek
setPreferences(prev => ({
  ...prev,
  theme: 'light'
}));

useEffect Hook'u ve Bağımlılık Dizisi

useEffect hook'u yan etkileri yönetmek için kullanılır, ancak bağımlılık dizisinin yanlış kullanımı sonsuz döngülere veya eksik güncellemelere neden olabilir.

// ❌ Yanlış Kullanım
useEffect(() => {
  fetchUserData(userId);
}, []); // Eksik bağımlılık

// ✅ Doğru Kullanım
useEffect(() => {
  fetchUserData(userId);
}, [userId]); // userId değiştiğinde effect tetiklenir

// 🚀 İleri Seviye Kullanım
useEffect(() => {
  const controller = new AbortController();
  
  async function fetchData() {
    try {
      const response = await fetch(`/api/users/${userId}`, {
        signal: controller.signal
      });
      const data = await response.json();
      setUser(data);
    } catch (error) {
      if (!error.name === 'AbortError') {
        setError(error);
      }
    }
  }
  
  fetchData();
  
  return () => controller.abort(); // Cleanup
}, [userId]);

useMemo ve useCallback ile Performans Optimizasyonu

Gereksiz hesaplamaları ve re-render'ları önlemek için useMemo ve useCallback hook'larını kullanabiliriz.

// ❌ Yanlış Kullanım
const sortedItems = items.sort((a, b) => b.value - a.value);

// ✅ Doğru Kullanım - useMemo
const sortedItems = useMemo(() => {
  return items.sort((a, b) => b.value - a.value);
}, [items]);

// ❌ Yanlış Kullanım
const handleSearch = (query) => {
  setSearchResults(items.filter(item => 
    item.name.toLowerCase().includes(query.toLowerCase())
  ));
};

// ✅ Doğru Kullanım - useCallback
const handleSearch = useCallback((query) => {
  setSearchResults(items.filter(item => 
    item.name.toLowerCase().includes(query.toLowerCase())
  ));
}, [items]);

Custom Hooks ile Kod Tekrarını Önleme

Tekrar eden mantığı custom hook'lar içinde soyutlayarak kod tekrarını önleyebilir ve bakımı kolaylaştırabiliriz.

// ✅ Custom Hook Örneği
function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = useCallback((value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  }, [key, storedValue]);

  return [storedValue, setValue];
}

// Kullanımı
function App() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Temayı Değiştir
    </button>
  );
}

useReducer ile Kompleks State Yönetimi

Karmaşık state mantığını yönetmek için useReducer hook'unu kullanabiliriz.

// Action Types
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const DELETE_TODO = 'DELETE_TODO';

// Reducer
function todoReducer(state, action) {
  switch (action.type) {
    case ADD_TODO:
      return [...state, {
        id: Date.now(),
        text: action.payload,
        completed: false
      }];
    case TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.payload
          ? { ...todo, completed: !todo.completed }
          : todo
      );
    case DELETE_TODO:
      return state.filter(todo => todo.id !== action.payload);
    default:
      return state;
  }
}

// Component
function TodoList() {
  const [todos, dispatch] = useReducer(todoReducer, []);

  const addTodo = (text) => {
    dispatch({ type: ADD_TODO, payload: text });
  };

  const toggleTodo = (id) => {
    dispatch({ type: TOGGLE_TODO, payload: id });
  };

  const deleteTodo = (id) => {
    dispatch({ type: DELETE_TODO, payload: id });
  };

  return (
    // JSX
  );
}

Sonuç

React Hooks, modern React uygulamalarının vazgeçilmez bir parçasıdır. Doğru kullanıldığında, kodunuzu daha temiz, bakımı kolay ve performanslı hale getirebilirsiniz. Bu yazıda bahsedilen best practice'leri uygulayarak, daha iyi React uygulamaları geliştirebilirsiniz.

Önemli noktaları özetleyelim:

  • State'leri mantıklı bir şekilde bölün
  • Effect'lerin bağımlılıklarını doğru yönetin
  • Gereksiz hesaplamaları useMemo ve useCallback ile optimize edin
  • Tekrar eden mantığı custom hook'lar ile soyutlayın
  • Karmaşık state yönetimi için useReducer kullanın

React Hooks hakkında daha fazla bilgi için React resmi dokümantasyonunu inceleyebilirsiniz.