Durum Yönetimi Nedir? Durum yönetimi (State Management), uygulamanızın verilerinin nasıl saklandığı, güncellendiği ve bileşenler arasında nasıl paylaşıldığını kontrol eden sistemdir. React Native uygulamalarında etkili durum yönetimi, kullanıcı deneyimini ve performansı doğrudan etkiler.
Durum Yönetiminin Önemi: Veri tutarlılığını sağlar Bileşenler arası iletişimi kolaylaştırır Uygulamanın öngörülebilirliğini artırır Debugging ve test etmeyi kolaylaştırır Performansı optimize eder useState Hook'u useState, React'in en temel durum yönetimi hook'udur. Fonksiyonel bileşenlerde yerel durum oluşturmak için kullanılır.
Kopyala import React, { useState } from 'react'; import { View, Text, Button, StyleSheet } from 'react-native'; const CounterComponent = () => { // useState hook'u ile sayaç durumu oluşturma const [count, setCount] = useState(0); const [name, setName] = useState('Kullanıcı'); const [isVisible, setIsVisible] = useState(true); const increment = () => { setCount(count + 1); // veya fonksiyonel güncelleme: // setCount(prevCount => prevCount + 1); }; const decrement = () => { setCount(prevCount => prevCount - 1); }; const reset = () => { setCount(0); }; return ( <View style={styles.container}> <Text style={styles.title}>Sayaç Uygulaması</Text> {isVisible && ( <Text style={styles.counter}>Sayı: {count}</Text> )} <Text style={styles.greeting}>Merhaba, {name}!</Text> <View style={styles.buttonContainer}> <Button title="Artır" onPress={increment} /> <Button title="Azalt" onPress={decrement} /> <Button title="Sıfırla" onPress={reset} /> <Button title={isVisible ? "Gizle" : "Göster"} onPress={() => setIsVisible(!isVisible)} /> </View> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, }, counter: { fontSize: 48, fontWeight: 'bold', color: '#3498db', marginBottom: 20, }, greeting: { fontSize: 18, marginBottom: 30, color: '#2c3e50', }, buttonContainer: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', gap: 10, }, }); export default CounterComponent;
useState Simülasyonu 0
Artır Azalt Sıfırla
Butonlara tıklayarak useState'in nasıl çalıştığını görün
useEffect Hook'u useEffect, bileşenin yaşam döngüsü olaylarını yönetmek için kullanılır. API çağrıları, zamanlayıcılar ve temizlik işlemleri için idealdir.
Kopyala import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet } from 'react-native'; const TimerComponent = () => { const [seconds, setSeconds] = useState(0); const [isActive, setIsActive] = useState(true); // Bileşen mount olduğunda çalışır useEffect(() => { console.log('Bileşen mount oldu'); // Cleanup fonksiyonu - bileşen unmount olduğunda çalışır return () => { console.log('Bileşen unmount oldu'); }; }, []); // Boş dependency array - sadece mount/unmount'ta çalışır // seconds değiştiğinde çalışır useEffect(() => { console.log('Saniye değişti:', seconds); }, [seconds]); // seconds dependency'si // Timer effect'i useEffect(() => { let interval = null; if (isActive) { interval = setInterval(() => { setSeconds(prevSeconds => prevSeconds + 1); }, 1000); } else if (!isActive && seconds !== 0) { clearInterval(interval); } // Cleanup fonksiyonu return () => clearInterval(interval); }, [isActive, seconds]); // API çağrısı örneği useEffect(() => { const fetchData = async () => { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); console.log('Veri alındı:', data); } catch (error) { console.error('Hata:', error); } }; fetchData(); }, []); // Sadece mount'ta çalışır const formatTime = (totalSeconds) => { const minutes = Math.floor(totalSeconds / 60); const secs = totalSeconds % 60; return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; return ( <View style={styles.container}> <Text style={styles.title}>Zamanlayıcı</Text> <Text style={styles.timer}>{formatTime(seconds)}</Text> <Text style={styles.status}> Durum: {isActive ? 'Çalışıyor' : 'Durduruldu'} </Text> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, }, timer: { fontSize: 48, fontWeight: 'bold', color: '#e74c3c', marginBottom: 10, }, status: { fontSize: 16, color: '#7f8c8d', }, }); export default TimerComponent;
🔄 useEffect Dependency Patterns Dependency Array Çalışma Zamanı Kullanım Alanı useEffect(() => {}, []) Sadece mount/unmount API çağrıları, event listener'lar useEffect(() => {}, [value]) value değiştiğinde Belirli state değişikliklerini izleme useEffect(() => {}) Her render'da Performans sorunlarına neden olabilir
Context API ile Global State Context API, prop drilling problemini çözmek ve global state yönetimi için React'in yerleşik çözümüdür. Küçük ve orta ölçekli uygulamalar için idealdir.
Kopyala import React, { createContext, useContext, useReducer } from 'react'; import { View, Text, Button, StyleSheet } from 'react-native'; // 1. Context oluşturma const AppContext = createContext(); // 2. Initial state const initialState = { user: null, theme: 'light', notifications: [], isLoading: false, }; // 3. Reducer fonksiyonu const appReducer = (state, action) => { switch (action.type) { case 'SET_USER': return { ...state, user: action.payload }; case 'SET_THEME': return { ...state, theme: action.payload }; case 'ADD_NOTIFICATION': return { ...state, notifications: [...state.notifications, action.payload] }; case 'SET_LOADING': return { ...state, isLoading: action.payload }; case 'CLEAR_NOTIFICATIONS': return { ...state, notifications: [] }; default: return state; } }; // 4. Context Provider bileşeni export const AppProvider = ({ children }) => { const [state, dispatch] = useReducer(appReducer, initialState); // Action creators const setUser = (user) => { dispatch({ type: 'SET_USER', payload: user }); }; const setTheme = (theme) => { dispatch({ type: 'SET_THEME', payload: theme }); }; const addNotification = (notification) => { dispatch({ type: 'ADD_NOTIFICATION', payload: notification }); }; const setLoading = (isLoading) => { dispatch({ type: 'SET_LOADING', payload: isLoading }); }; const clearNotifications = () => { dispatch({ type: 'CLEAR_NOTIFICATIONS' }); }; const value = { ...state, setUser, setTheme, addNotification, setLoading, clearNotifications, }; return ( <AppContext.Provider value={value}> {children} </AppContext.Provider> ); }; // 5. Custom hook export const useApp = () => { const context = useContext(AppContext); if (!context) { throw new Error('useApp must be used within AppProvider'); } return context; }; // 6. Bileşenlerde kullanım const UserProfile = () => { const { user, setUser, theme, setTheme } = useApp(); const handleLogin = () => { setUser({ id: 1, name: 'Ahmet Yılmaz', email: 'ahmet@example.com' }); }; const toggleTheme = () => { setTheme(theme === 'light' ? 'dark' : 'light'); }; return ( <View style={[ styles.container, { backgroundColor: theme === 'light' ? '#fff' : '#2c3e50' } ]}> <Text style={[ styles.title, { color: theme === 'light' ? '#2c3e50' : '#fff' } ]}> Kullanıcı Profili </Text> {user ? ( <View> <Text style={styles.userInfo}>İsim: {user.name}</Text> <Text style={styles.userInfo}>Email: {user.email}</Text> </View> ) : ( <Text style={styles.noUser}>Giriş yapılmamış</Text> )} <Button title="Giriş Yap" onPress={handleLogin} /> <Button title="Tema Değiştir" onPress={toggleTheme} /> </View> ); }; // 7. Ana uygulama const App = () => { return ( <AppProvider> <UserProfile /> </AppProvider> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, }, userInfo: { fontSize: 16, marginBottom: 10, color: '#34495e', }, noUser: { fontSize: 16, color: '#7f8c8d', marginBottom: 20, }, }); export default App;
Pratik Örnek: Todo Uygulaması Context API kullanarak basit bir Todo uygulaması oluşturalım:
Todo Uygulaması Simülasyonu Ekle
Toplam: 0 görev
Kopyala import React, { createContext, useContext, useReducer } from 'react'; import { View, Text, TextInput, Button, FlatList, StyleSheet } from 'react-native'; // Todo Context const TodoContext = createContext(); const 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) }; case 'SET_FILTER': return { ...state, filter: action.payload }; default: return state; } }; export const TodoProvider = ({ children }) => { const [state, dispatch] = useReducer(todoReducer, { todos: [], filter: 'all' // all, active, completed }); const addTodo = (text) => { if (text.trim()) { dispatch({ type: 'ADD_TODO', payload: text.trim() }); } }; const toggleTodo = (id) => { dispatch({ type: 'TOGGLE_TODO', payload: id }); }; const deleteTodo = (id) => { dispatch({ type: 'DELETE_TODO', payload: id }); }; const setFilter = (filter) => { dispatch({ type: 'SET_FILTER', payload: filter }); }; const getFilteredTodos = () => { switch (state.filter) { case 'active': return state.todos.filter(todo => !todo.completed); case 'completed': return state.todos.filter(todo => todo.completed); default: return state.todos; } }; const value = { todos: getFilteredTodos(), allTodos: state.todos, filter: state.filter, addTodo, toggleTodo, deleteTodo, setFilter, }; return ( <TodoContext.Provider value={value}> {children} </TodoContext.Provider> ); }; export const useTodos = () => { const context = useContext(TodoContext); if (!context) { throw new Error('useTodos must be used within TodoProvider'); } return context; }; // Todo bileşenleri const TodoInput = () => { const [text, setText] = useState(''); const { addTodo } = useTodos(); const handleSubmit = () => { addTodo(text); setText(''); }; return ( <View style={styles.inputContainer}> <TextInput style={styles.input} value={text} onChangeText={setText} placeholder="Yeni görev ekleyin..." onSubmitEditing={handleSubmit} /> <Button title="Ekle" onPress={handleSubmit} /> </View> ); }; const TodoItem = ({ todo }) => { const { toggleTodo, deleteTodo } = useTodos(); return ( <View style={styles.todoItem}> <Text style={[ styles.todoText, todo.completed && styles.completedText ]} onPress={() => toggleTodo(todo.id)} > {todo.text} </Text> <Button title="Sil" onPress={() => deleteTodo(todo.id)} color="#e74c3c" /> </View> ); }; const TodoApp = () => { const { todos, allTodos, filter, setFilter } = useTodos(); const completedCount = allTodos.filter(todo => todo.completed).length; const activeCount = allTodos.filter(todo => !todo.completed).length; return ( <View style={styles.container}> <Text style={styles.title}>Todo Uygulaması</Text> <TodoInput /> <View style={styles.filterContainer}> <Button title="Tümü" onPress={() => setFilter('all')} color={filter === 'all' ? '#3498db' : '#95a5a6'} /> <Button title="Aktif" onPress={() => setFilter('active')} color={filter === 'active' ? '#3498db' : '#95a5a6'} /> <Button title="Tamamlanan" onPress={() => setFilter('completed')} color={filter === 'completed' ? '#3498db' : '#95a5a6'} /> </View> <FlatList data={todos} keyExtractor={(item) => item.id.toString()} renderItem={({ item }) => <TodoItem todo={item} />} style={styles.list} /> <Text style={styles.stats}> Aktif: {activeCount}, Tamamlanan: {completedCount} </Text> </View> ); }; const App = () => { return ( <TodoProvider> <TodoApp /> </TodoProvider> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 20, backgroundColor: '#f8f9fa', }, title: { fontSize: 24, fontWeight: 'bold', textAlign: 'center', marginBottom: 20, color: '#2c3e50', }, inputContainer: { flexDirection: 'row', marginBottom: 20, gap: 10, }, input: { flex: 1, borderWidth: 1, borderColor: '#ddd', padding: 10, borderRadius: 5, backgroundColor: 'white', }, filterContainer: { flexDirection: 'row', justifyContent: 'space-around', marginBottom: 20, }, list: { flex: 1, }, todoItem: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 15, backgroundColor: 'white', marginBottom: 5, borderRadius: 5, }, todoText: { flex: 1, fontSize: 16, color: '#2c3e50', }, completedText: { textDecorationLine: 'line-through', color: '#7f8c8d', }, stats: { textAlign: 'center', marginTop: 20, fontSize: 16, color: '#7f8c8d', }, }); export default App;
Redux ile Gelişmiş State Yönetimi Büyük ve karmaşık uygulamalar için Redux, daha güçlü ve öngörülebilir state yönetimi sunar. Redux Toolkit kullanarak modern Redux geliştirme yapabilirsiniz.
Redux Ne Zaman Kullanılmalı? Çok sayıda bileşen aynı state'i paylaşıyorsa State güncellemeleri karmaşık iş mantığı gerektiriyorsa State değişikliklerini izlemeniz gerekiyorsa Time-travel debugging özelliği istiyorsanız Kopyala // Redux Toolkit kurulumu npm install @reduxjs/toolkit react-redux // store/counterSlice.js import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: { value: 0, history: [], }, reducers: { increment: (state) => { state.value += 1; state.history.push(`Artırıldı: ${state.value}`); }, decrement: (state) => { state.value -= 1; state.history.push(`Azaltıldı: ${state.value}`); }, incrementByAmount: (state, action) => { state.value += action.payload; state.history.push(`${action.payload} eklendi: ${state.value}`); }, reset: (state) => { state.value = 0; state.history.push('Sıfırlandı'); }, }, }); export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions; export default counterSlice.reducer; // store/index.js import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; export const store = configureStore({ reducer: { counter: counterReducer, }, }); // App.js import React from 'react'; import { Provider } from 'react-redux'; import { store } from './store'; import CounterApp from './CounterApp'; const App = () => { return ( <Provider store={store}> <CounterApp /> </Provider> ); }; export default App; // CounterApp.js import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { View, Text, Button, StyleSheet } from 'react-native'; import { increment, decrement, incrementByAmount, reset } from './store/counterSlice'; const CounterApp = () => { const count = useSelector((state) => state.counter.value); const history = useSelector((state) => state.counter.history); const dispatch = useDispatch(); return ( <View style={styles.container}> <Text style={styles.title}>Redux Counter</Text> <Text style={styles.counter}>{count}</Text> <View style={styles.buttonContainer}> <Button title="+" onPress={() => dispatch(increment())} /> <Button title="-" onPress={() => dispatch(decrement())} /> <Button title="+5" onPress={() => dispatch(incrementByAmount(5))} /> <Button title="Reset" onPress={() => dispatch(reset())} /> </View> <Text style={styles.historyTitle}>Geçmiş:</Text> {history.slice(-5).map((item, index) => ( <Text key={index} style={styles.historyItem}>{item}</Text> ))} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, }, counter: { fontSize: 48, fontWeight: 'bold', color: '#3498db', marginBottom: 30, }, buttonContainer: { flexDirection: 'row', gap: 10, marginBottom: 30, }, historyTitle: { fontSize: 18, fontWeight: 'bold', marginBottom: 10, }, historyItem: { fontSize: 14, color: '#7f8c8d', marginBottom: 5, }, }); export default CounterApp;
📊 State Yönetimi Karşılaştırması Yöntem Kullanım Alanı Avantajlar Dezavantajlar useState Yerel bileşen state'i Basit, hızlı Sadece yerel kullanım Context API Orta ölçekli uygulamalar Yerleşik, prop drilling çözümü Performans sorunları olabilir Redux Büyük, karmaşık uygulamalar Öngörülebilir, debug araçları Karmaşık kurulum, boilerplate Zustand Orta-büyük uygulamalar Basit API, TypeScript desteği Daha az topluluk desteği
Sonraki Adımlar Bu derste React Native'de durum yönetimini öğrendiniz. Bir sonraki derste API entegrasyonu konusunu ele alacak ve uygulamanızı backend servislerle nasıl bağlayacağınızı öğreneceksiniz.
Bu Derste Öğrendikleriniz: useState ile yerel state yönetimi useEffect ile yaşam döngüsü yönetimi Context API ile global state useReducer ile karmaşık state mantığı Redux ile gelişmiş state yönetimi State yönetimi yöntemlerinin karşılaştırması