React'ta Modern Hooks ve Design Patterns: Kapsamlı Rehber
React ekosistemi sürekli gelişiyor ve modern web uygulamaları geliştirmek için yeni pattern'lar ve best practice'ler ortaya çıkıyor. Bu yazıda, React uygulamalarınızı daha iyi hale getirecek modern hooks ve design pattern'ları inceleyeceğiz.
Neden Modern Pattern'lar Önemli?
Modern React pattern'ları ve hooks'ları kullanmanın birçok avantajı var:
- Kod Tekrarını Azaltma: Custom hooks ile ortak mantığı tek bir yerde toplayabilirsiniz
- Daha İyi Performans: Memoization ve optimizasyon teknikleri ile uygulamanızı hızlandırabilirsiniz
- Bakımı Kolay Kod: Design pattern'lar ile kodunuzu daha organize ve anlaşılır hale getirebilirsiniz
- Yeniden Kullanılabilirlik: Modüler ve taşınabilir bileşenler oluşturabilirsiniz
Bu rehberde, bu avantajları nasıl elde edebileceğinizi pratik örneklerle göreceğiz.
Custom Hooks: Yeniden Kullanılabilir Mantık
useLocalStorage Hook'u
Tarayıcı local storage'ını React state ile senkronize eden bir custom hook örneği:
function useLocalStorage<T>(key: string, initialValue: T) { // State'i başlat const [storedValue, setStoredValue] = useState<T>(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.error(error); return initialValue; } }); // State değiştiğinde localStorage'ı güncelle useEffect(() => { try { window.localStorage.setItem(key, JSON.stringify(storedValue)); } catch (error) { console.error(error); } }, [key, storedValue]); return [storedValue, setStoredValue] as const; } // Kullanım örneği: function App() { const [theme, setTheme] = useLocalStorage('theme', 'light'); return ( <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> Temayı Değiştir </button> ); }
useDebounce Hook'u
API çağrıları veya ağır işlemleri optimize etmek için debounce hook'u:
function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState<T>(value); useEffect(() => { const timer = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(timer); }; }, [value, delay]); return debouncedValue; } // Kullanım örneği: function SearchComponent() { const [search, setSearch] = useState(''); const debouncedSearch = useDebounce(search, 500); useEffect(() => { // API çağrısı fetchSearchResults(debouncedSearch); }, [debouncedSearch]); return ( <input type="text" value={search} onChange={(e) => setSearch(e.target.value)} /> ); }
Modern React Pattern'ları
Compound Components Pattern
Birlikte çalışan, esnek ve yeniden kullanılabilir bileşenler oluşturma:
// Accordion bileşeni örneği const AccordionContext = createContext<{ activeIndex: number; setActiveIndex: (index: number) => void; } | null>(null); function Accordion({ children }: { children: React.ReactNode }) { const [activeIndex, setActiveIndex] = useState(0); return ( <AccordionContext.Provider value={{ activeIndex, setActiveIndex }}> <div className="accordion">{children}</div> </AccordionContext.Provider> ); } function AccordionItem({ children, index }: { children: React.ReactNode; index: number }) { const context = useContext(AccordionContext); if (!context) throw new Error('AccordionItem must be used within Accordion'); const { activeIndex, setActiveIndex } = context; const isActive = activeIndex === index; return ( <div className="accordion-item"> {React.Children.map(children, child => { if (React.isValidElement(child)) { return React.cloneElement(child, { isActive }); } return child; })} </div> ); } // Kullanım örneği: function App() { return ( <Accordion> <AccordionItem index={0}> <AccordionHeader>Başlık 1</AccordionHeader> <AccordionContent>İçerik 1</AccordionContent> </AccordionItem> <AccordionItem index={1}> <AccordionHeader>Başlık 2</AccordionHeader> <AccordionContent>İçerik 2</AccordionContent> </AccordionItem> </Accordion> ); }
Render Props Pattern
Bileşen mantığını paylaşmanın esnek bir yolu:
interface MousePositionProps { render: (position: { x: number; y: number }) => React.ReactNode; } function MousePosition({ render }: MousePositionProps) { const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { function handleMouseMove(e: MouseEvent) { setPosition({ x: e.clientX, y: e.clientY }); } window.addEventListener('mousemove', handleMouseMove); return () => window.removeEventListener('mousemove', handleMouseMove); }, []); return <>{render(position)}</>; } // Kullanım örneği: function App() { return ( <MousePosition render={({ x, y }) => ( <div> Fare pozisyonu: {x}, {y} </div> )} /> ); }
Performance Optimizasyonları
useMemo ve useCallback
Gereksiz render'ları önlemek için memoization kullanımı:
function ExpensiveComponent({ data, onItemSelect }: Props) { // Ağır hesaplama gerektiren işlemleri memoize et const processedData = useMemo(() => { return data.map(item => ({ ...item, calculated: expensiveCalculation(item) })); }, [data]); // Event handler'ları memoize et const handleSelect = useCallback((item: Item) => { onItemSelect(item); }, [onItemSelect]); return ( <ul> {processedData.map(item => ( <li key={item.id} onClick={() => handleSelect(item)}> {item.calculated} </li> ))} </ul> ); }
React.memo ile Bileşen Memoization
Alt bileşenlerin gereksiz render'larını önleme:
interface ItemProps { item: Item; onSelect: (item: Item) => void; } const ListItem = React.memo(function ListItem({ item, onSelect }: ItemProps) { return ( <li onClick={() => onSelect(item)}> {item.name} </li> ); }); // Özel karşılaştırma fonksiyonu ile kullanım const ListItemWithCustomComparison = React.memo( ListItem, (prevProps, nextProps) => { return prevProps.item.id === nextProps.item.id; } );
Error Boundaries
Hata yönetimi için modern yaklaşım:
class ErrorBoundary extends React.Component< { children: React.ReactNode }, { hasError: boolean } > { constructor(props: { children: React.ReactNode }) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError() { return { hasError: true }; } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { console.error('Error caught by boundary:', error, errorInfo); } render() { if (this.state.hasError) { return <h1>Bir şeyler yanlış gitti.</h1>; } return this.props.children; } } // Kullanım örneği: function App() { return ( <ErrorBoundary> <ComponentThatMightError /> </ErrorBoundary> ); }
Sonuç
Modern React geliştirmede, custom hooks ve design pattern'ların doğru kullanımı, kodunuzu daha maintainable ve performanslı hale getirir. Bu yazıda öğrendiğimiz pattern'ları kendi projelerinizde kullanarak:
- Kod tekrarını azaltabilir
- Bileşen mantığını daha iyi organize edebilir
- Performans optimizasyonlarını doğru şekilde uygulayabilir
- Daha sürdürülebilir uygulamalar geliştirebilirsiniz
Bu pattern'ları ve hooks'ları kullanırken, her zaman use-case'inize en uygun çözümü seçmeye özen gösterin ve gereksiz optimizasyonlardan kaçının.