Hören Sie auf, bei jedem React-Problem automatisch auf useEffect zurückzugreifen.
Die Effekte synchronisieren sich mit externen Systemen; sie werden bei der Renderung erzeugt, Klicks werden in Events verarbeitet, und Daten werden mit Tools für echte Daten abgerufen.
Die useEffect-Falle
Lernende, die Hooks nutzen, greifen oft auf useEffect zurück, um Daten abzurufen, Zustände zu berechnen oder auf Knopfklicks zu reagieren. Effects synchronisieren sich mit externen Systemen – sie sind nicht das Standardwerkzeug für gewöhnliche Render-Logik.
Was useEffect tatsächlich tut
Nach dem Render führt React Effects aus, um Zuhörer hinzuzufügen, Verbindungen herzustellen oder imperativ mit etwas außerhalb von React zu kommunizieren. Falsche Verwendung führt zu Flackern, doppelten Abfragen und Rennfehlern.
// ❌ BAD: Two renders, unnecessary state
function UserProfile({ firstName, lastName }) {
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
return <h1>{fullName}</h1>;
}
// ✅ GOOD: Calculate during render
function UserProfile({ firstName, lastName }) {
const fullName = `${firstName} ${lastName}`;
return <h1>{fullName}</h1>;
}
// ❌ BAD: Unnecessary effect + state
function ProductList({ products }) {
const [sortedProducts, setSortedProducts] = useState([]);
useEffect(() => {
const sorted = [...products].sort((a, b) => a.price - b.price);
setSortedProducts(sorted);
}, [products]);
return (
<ul>
{sortedProducts.map(product => (
<li key={product.id}>{product.name} - ${product.price}</li>
))}
</ul>
);
}
// ✅ GOOD: Calculate during render (useMemo for expensive ops)
function ProductList({ products }) {
const sortedProducts = useMemo(() => {
return [...products].sort((a, b) => a.price - b.price);
}, [products]);
return (
<ul>
{sortedProducts.map(product => (
<li key={product.id}>{product.name} - ${product.price}</li>
))}
</ul>
);
}
// ❌ BAD: Manual fetching with useEffect
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('Failed to fetch');
const data = await response.json();
if (!cancelled) setUser(data);
} catch (err) {
if (!cancelled) setError(err.message);
} finally {
if (!cancelled) setLoading(false);
}
}
fetchUser();
return () => { cancelled = true; };
}, [userId]);
if (loading) return <Spinner />;
if (error) return <p>Error: {error}</p>;
return <div>{user.name}</div>;
}
// ✅ GOOD: Use TanStack Query (React Query)
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }) {
const { data: user, isPending, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
});
if (isPending) return <Spinner />;
if (error) return <p>Error loading user</p>;
return <div>{user.name}</div>;
}
// ❌ BAD: Overcomplicating with useEffect
function LikeButton({ postId }) {
const [liked, setLiked] = useState(false);
useEffect(() => {
if (liked) {
fetch(`/api/posts/${postId}/like`, { method: 'POST' });
} else {
fetch(`/api/posts/${postId}/unlike`, { method: 'POST' });
}
}, [liked, postId]);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'}
</button>
);
}
// ✅ GOOD: Handle in the event handler
function LikeButton({ postId }) {
const [liked, setLiked] = useState(false);
const handleLike = async () => {
const newLiked = !liked;
setLiked(newLiked);
await fetch(`/api/posts/${postId}/${newLiked ? 'like' : 'unlike'}`, {
method: 'POST',
});
};
return (
<button onClick={handleLike}>
{liked ? '❤️' : '🤍'}
</button>
);
}
function WindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
// Cleanup is crucial!
return () => window.removeEventListener('resize', handleResize);
}, []);
return <p>Window width: {width}px</p>;
}
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createWebSocketConnection(roomId);
connection.onMessage = (msg) => {
setMessages(prev => [...prev, msg]);
};
connection.connect();
return () => {
connection.disconnect(); // Cleanup!
};
}, [roomId]);
return <MessageList messages={messages} />;
}
function ChartComponent({ data }) {
const chartRef = useRef(null);
const chartInstance = useRef(null);
useEffect(() => {
if (chartRef.current && !chartInstance.current) {
chartInstance.current = new ChartLibrary(chartRef.current, {
type: 'line',
data: data,
});
}
return () => {
chartInstance.current?.destroy();
chartInstance.current = null;
};
}, []);
return <canvas ref={chartRef} />;
}
function PageTitle({ title }) {
useEffect(() => {
document.title = `${title} | My App`;
return () => {
document.title = 'My App'; // Reset on unmount
};
}, [title]);
return null;
}
Vier häufige Fehler
1. Wertberechnungen in Effects
Erstellen Sie während des Renderns (oder mit useMemo, wenn es aufwändig ist). Effekte, die über setState aus Props/Zustand agieren, enthalten oft verbotene Rückkopplungsschleifen.
2. Aufwändige Berechnungen in Effekten
Lassen Sie reine Berechnungen im Rendern/memo stattfinden, nicht nach dem Zeichnen.
3. Datensuche als unkontrollierter Effekt
Die Datensuche erfordert Stornierung, Caching sowie eine angemessene Fehlerbenutzeroberfläche. Verwenden Sie lieber spezielle Bibliotheken oder Frameworks mit Ladebegrenzungen; falls Effekte verwendet werden, müssen Abbrüche und Konflikte behandelt werden.
4. Benutzeraktionen in Effekten
Klick-Handler gehören zu Events. Effekte, die durch einen Klick ausgelöst werden, führen zu Indirektheit und Fehlern.
Entscheidungsbaum
Falls etwas aus vorhandenem Zustand/Props berechnet wird → Rendern. Falls es auf einen Klick/Absenden reagiert → Event-Handler. Falls es mit einer DOM-API, einem Netzwerkfeed oder dem Lebenszyklus eines Drittanbieter-Widgets kommuniziert → Effekt.
Wann Effekte sinnvoll sind
Außereigene Ereignislistener
WebSockets
Initialisierung von Drittanbieter-Bibliotheken
Imperative, nicht-React-APIs, die sauber bereinigt werden müssen
Fazit
useEffect nur zur Synchronisierung mit Systemen außerhalb von React – nicht als Standardwerkzeug für jeden Gedanken nach dem Rendern.
Im Strict Mode werden Effekte in der Entwicklung doppelt aufgerufen, um fehlende Bereinigungen aufzuzeigen – betrachten Sie das als Vorteil bei der Verwendung von Sockets. Wählen Sie deklarative Datenbibliotheken für den Serverzustand, damit Komponenten Abhängigkeiten beschreiben statt selbst Fetch-Effekte zu implementieren. Prüfen Sie vor dem Hinzufügen von Effekten, die lediglich Props in den Zustand kopieren, ob dieses Muster nicht auf einem Missverständnis des Renderings beruht.
Bereinigungsfunktionen müssen Listener und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich setState-on-unmounted.
Abhängigkeitsarrays sind Teil des Vertrags – wenn Werte weggelassen werden, treten veraltete Schließfunktionen auf; wenn zu viele Objekte und Effekte aufgelistet werden, führt das zu Leistungsproblemen bei jeder Darstellung.
Beim Synchronisieren mit URL-Suchparametern oder externen Speichern sollten speziell dafür entwickelte Hooks bevorzugt werden, die den Effektbereich kapseln.
Serverseitig generierte Apps sollten ohne Schutzmechanismen keine ausschließlich im Browser laufenden Effekte starten; dies sollte anhand der Umgebung oder des Lebenszyklus gesteuert werden.
Aufräumfunktionen müssen Zuhörer und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich setState-on-unmounted.
Abhängigkeitsarrays sind Teil des Vertrags – wenn Werte weggelassen werden, treten veraltete Schließfunktionen auf; wenn zu viele Objekte und Effekte aufgelistet werden, führt das zu Leistungsproblemen bei jeder Darstellung.
Beim Synchronisieren mit URL-Suchparametern oder externen Speichern sollten speziell dafür entwickelte Hooks bevorzugt werden, die den Effektbereich kapseln.
Server-seitig generierte Apps sollten ohne Schutzmechanismen keine ausschließlich im Browser wirkenden Effekte starten; dies muss anhand der Umgebung oder des Lebenszyklus kontrolliert werden.
Cleanup-Funktionen müssen Listener und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich von setState-on-unmounted.
Abhängigkeitsarrays sind Teil des Vertrags – wenn Werte weggelassen werden, treten veraltete Schließfunktionen auf; wenn zu viele Objekte und Effekte aufgelistet werden, führt dies bei jeder Renderung zu Problemen.
Beim Synchronisieren mit URL-Suchparametern oder externen Speichern sollten speziell dafür entwickelte Hooks verwendet werden, die den Effektbereich abgrenzen.
Server-seitig generierte Apps sollten ohne Schutzmechanismen keine ausschließlich im Browser wirkenden Effekte starten; dies muss anhand der Umgebung oder des Lebenszyklus kontrolliert werden.
Cleanup-Funktionen müssen Listener und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich von setState-on-unmounted.
Abhängigkeitsarrays sind Teil des Vertrags – wenn Werte weggelassen werden, treten veraltete Schließfunktionen auf; wenn zu viele Objekte und Effekte aufgelistet werden, führt das zu Leistungsproblemen bei jeder Darstellung.
Beim Synchronisieren mit URL-Suchparametern oder externen Speichern sollten speziell dafür entwickelte Hooks bevorzugt werden, die den Effektbereich kapseln.
Serverseitig generierte Apps sollten ohne Schutzmechanismen keine ausschließlich im Browser laufenden Effekte starten; dies sollte anhand der Umgebung oder des Lebenszyklus gesteuert werden.
Aufräumfunktionen müssen Zuhörer und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich setState-on-unmounted.
Abhängigkeitsarrays sind Teil des Vertrags – wenn Werte weggelassen werden, treten veraltete Schließfunktionen auf; wenn zu viele Objekte und Effekte aufgelistet werden, führt das zu Leistungsproblemen bei jeder Darstellung.
Beim Synchronisieren mit URL-Suchparametern oder externen Speichern sollten speziell dafür entwickelte Hooks bevorzugt werden, die den Effektbereich kapseln.
Server-seitig generierte Apps sollten ohne Schutzmechanismen keine ausschließlich im Browser wirkenden Effekte starten; dies muss anhand der Umgebung oder des Lebenszyklus kontrolliert werden.
Cleanup-Funktionen müssen Listener und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich von setState-on-unmounted.
Abhängigkeitsarrays sind Teil des Vertrags – wenn Werte weggelassen werden, treten veraltete Schließfunktionen auf; wenn zu viele Objekte und Effekte aufgelistet werden, führt dies bei jeder Renderung zu Problemen.
Beim Synchronisieren mit URL-Suchparametern oder externen Speichern sollten speziell dafür entwickelte Hooks verwendet werden, die den Effektbereich abgrenzen.
Server-seitig generierte Apps sollten ohne Schutzmechanismen keine ausschließlich im Browser wirkenden Effekte starten; dies muss anhand der Umgebung oder des Lebenszyklus kontrolliert werden.
Cleanup-Funktionen müssen Listener und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich von setState-on-unmounted.
Abhängigkeitsarrays sind Teil des Vertrags – wenn Werte weggelassen werden, treten veraltete Schließfunktionen auf; wenn zu viele Objekte und Effekte aufgelistet werden, führt das zu übermäßigen Render-Vorgängen.
Beim Synchronisieren mit URL-Suchparametern oder externen Speichern sollten speziell dafür entwickelte Hooks bevorzugt werden, die den Effektbereich abgrenzen.
Serverseitig gerenderte Apps sollten ohne Schutzmechanismen keine ausschließlich im Browser laufenden Effekte starten; dies sollte anhand der Umgebung oder des Lebenszyklus kontrolliert werden.
Reinigungsfunktionen müssen Zuhörer und Timer deaktivieren; verbleibende Handler sind eine klassische Ursache für Warnungen bezüglich setState-on-unmounted.