Einführung in Modernes React
React hat die Frontend-Entwicklung revolutioniert. Mit den in React 16.8 eingeführten Hooks ist das Schreiben von Komponenten einfacher und intuitiver geworden.
1. useState - Zustandsverwaltung
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: '', email: '' });
const increment = () => setCount(prev => prev + 1);
const updateUser = (field, value) => {
setUser(prev => ({ ...prev, [field]: value }));
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
<input
value={user.name}
onChange={(e) => updateUser('name', e.target.value)}
placeholder="Name"
/>
</div>
);
}
2. useEffect - Seiteneffekte
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(\`/api/users/\${userId}\`);
if (!response.ok) throw new Error('User not found');
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
// Cleanup-Funktion
return () => {
// Ausstehende Anfragen abbrechen falls nötig
};
}, [userId]); // Abhängigkeit: ausführen wenn userId sich ändert
if (loading) return <p>Laden...</p>;
if (error) return <p>Fehler: {error}</p>;
return <div>Willkommen, {user.name}!</div>;
}
3. useContext - Globaler Zustand
import { createContext, useContext, useState } from 'react';
// Context erstellen
const ThemeContext = createContext();
// Provider-Komponente
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Custom Hook für Context-Nutzung
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme muss innerhalb von ThemeProvider verwendet werden');
}
return context;
}
// Komponente die Theme verwendet
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={theme}>
<button onClick={toggleTheme}>
Wechseln zu {theme === 'light' ? 'dark' : 'light'}
</button>
</header>
);
}
4. Custom Hooks
// useLocalStorage - Zustand persistieren
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore = value instanceof Function
? value(storedValue)
: value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
// useFetch - Fetch mit Zustand
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortController = new AbortController();
fetch(url, { signal: abortController.signal })
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
return () => abortController.abort();
}, [url]);
return { data, loading, error };
}
5. useReducer für Komplexe Zustände
import { useReducer } from 'react';
const initialState = {
items: [],
loading: false,
error: null
};
function reducer(state, action) {
switch (action.type) {
case 'FETCH_START':
return { ...state, loading: true, error: null };
case 'FETCH_SUCCESS':
return { ...state, loading: false, items: action.payload };
case 'FETCH_ERROR':
return { ...state, loading: false, error: action.payload };
case 'ADD_ITEM':
return { ...state, items: [...state.items, action.payload] };
case 'REMOVE_ITEM':
return {
...state,
items: state.items.filter(item => item.id !== action.payload)
};
default:
return state;
}
}
function TodoList() {
const [state, dispatch] = useReducer(reducer, initialState);
const addTodo = (text) => {
dispatch({
type: 'ADD_ITEM',
payload: { id: Date.now(), text, completed: false }
});
};
return (
<div>
{state.items.map(item => (
<div key={item.id}>{item.text}</div>
))}
</div>
);
}
6. useMemo und useCallback
import { useMemo, useCallback } from 'react';
function ExpensiveList({ items, filter }) {
// Teure Berechnungen memorisieren
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
// Callback memorisieren um Re-Renders von Kindern zu vermeiden
const handleClick = useCallback((id) => {
console.log('Clicked:', id);
}, []);
return (
<ul>
{filteredItems.map(item => (
<ListItem
key={item.id}
item={item}
onClick={handleClick}
/>
))}
</ul>
);
}
React Best Practices
- Halten Sie Komponenten klein und fokussiert
- Verwenden Sie Custom Hooks für wiederverwendbare Logik
- Vermeiden Sie Prop Drilling mit Context API
- Memorisieren Sie mit useMemo/useCallback nur wenn nötig
- Behandeln Sie Fehler mit Error Boundaries
Fazit
React mit Hooks bietet eine elegante und leistungsstarke Möglichkeit, UIs zu erstellen. Beherrschen Sie diese Muster und Sie sind für jedes Projekt bereit!