Blog Tipps7 Min. Lesezeit

useState und useEffect erklärt – wofür braucht man die eigentlich?

Die zwei React-Hooks, die du in fast jedem Projekt antreffen wirst – und warum sie zusammen funktionieren, aber sehr unterschiedliche Jobs haben.

d6benjamin27. Mai 2026

Wenn du anfängst React zu lernen, begegnest du diesen zwei Hooks ständig – manchmal im selben Atemzug, als wären sie ein Paket. Sie lösen aber komplett unterschiedliche Probleme, und wer das nicht versteht, baut früher oder später Bugs ein, die schwer zu finden sind.


useState: Erinnerung für Komponenten

Eine React-Komponente ist im Grunde eine Funktion. Funktionen haben kein Gedächtnis – jedes Mal wenn sie aufgerufen werden, fangen sie von vorne an. useState gibt deiner Komponente die Fähigkeit, sich etwas zu merken.

import { useState } from 'react'

function Counter() {
  const [count, setCount] = useState(0)

  return (
    <button onClick={() => setCount(count + 1)}>
      {count} Mal geklickt
    </button>
  )
}

Das useState(0) gibt dir zwei Dinge zurück: den aktuellen Wert (count) und eine Funktion, um ihn zu ändern (setCount). Der entscheidende Punkt: Wenn du setCount aufrufst, rendert React die Komponente neu – mit dem aktualisierten Wert. Eine normale Variable würde sich nach jedem Render zurücksetzen.

Was sinnvoll in useState gehört

  • Formulareingaben, die sich der User tippt
  • Ob ein Modal offen oder geschlossen ist
  • Das aktuelle Tab in einer Navigation
  • Daten, die du von einer API geladen hast

Was nicht in useState gehört

Werte, die du direkt aus anderen State-Variablen berechnen kannst, brauchen kein eigenes useState. Wenn du firstName und lastName im State hast, ist fullName einfach eine Variable – kein eigener State:

const [firstName, setFirstName] = useState("")
const [lastName, setLastName] = useState("")

const fullName = `${firstName} ${lastName}`  // keine useState nötig

Einen abgeleiteten Wert in useState zu packen führt zu Synchronisierungsproblemen: Wann genau aktualisierst du fullName? Und was passiert, wenn du es vergisst?


useEffect: Reaktion auf Veränderungen

useEffect ist für Code zuständig, der als Reaktion auf etwas passieren soll – meistens wenn die Komponente zum ersten Mal erscheint oder wenn sich ein bestimmter Wert ändert.

Der klassische Anwendungsfall: Daten von einer API laden.

import { useState, useEffect } from 'react'

function UserProfile({ userId }: { userId: number }) {
  const [user, setUser] = useState<{ name: string } | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    setLoading(true)
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        setUser(data)
        setLoading(false)
      })
  }, [userId])

  if (loading) return <p>Lädt…</p>
  return <p>{user?.name}</p>
}

Das zweite Argument von useEffect – das Array [userId] – ist die Dependency List. Sie sagt React: „Führe diesen Effect aus, wenn die Komponente erscheint, und danach jedes Mal wenn sich userId ändert." Lässt du das Array komplett weg, läuft der Effect nach jedem Render – was fast nie das ist, was du willst.

Die drei Varianten

useEffect(() => {
  // läuft nach jedem Render – selten sinnvoll
})

useEffect(() => {
  // läuft einmal, wenn die Komponente erscheint
}, [])

useEffect(() => {
  // läuft wenn sich `value` ändert
}, [value])

Cleanup – der Teil, den viele übersehen

Manche Effects müssen aufgeräumt werden, wenn die Komponente verschwindet oder der Effect neu startet. Das Paradebeispiel ist ein Timer:

useEffect(() => {
  const interval = setInterval(() => {
    console.log("tick")
  }, 1000)

  return () => clearInterval(interval)  // Cleanup
}, [])

Ohne das return () => clearInterval(interval) läuft der Timer weiter, auch wenn die Komponente längst nicht mehr sichtbar ist. Bei API-Requests ist es ähnlich: Kommt die Antwort an, nachdem die Komponente schon weg ist, versuchst du State auf einer unmounted Komponente zu setzen.


Wie sie zusammenarbeiten

Das typische Muster in der Praxis: useState hält die Daten, useEffect holt sie.

function ArticleList() {
  const [articles, setArticles] = useState<Article[]>([])
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    fetch('/api/articles')
      .then(res => {
        if (!res.ok) throw new Error('Fehler beim Laden')
        return res.json()
      })
      .then(setArticles)
      .catch(err => setError(err.message))
  }, [])

  if (error) return <p>Fehler: {error}</p>
  return <ul>{articles.map(a => <li key={a.id}>{a.title}</li>)}</ul>
}

useEffect löst den Fetch aus, useState speichert das Ergebnis, React rendert neu. Das ist kein Sonderfall – das ist das Grundmuster hinter fast jeder Komponente, die externe Daten anzeigt.


Eine ehrliche Einschätzung

useEffect ist der Hook, der am häufigsten falsch verwendet wird. Nicht weil er kompliziert ist, sondern weil er verlockend ist: Man kann damit fast alles machen, und deshalb landet dort Code, der eigentlich gar kein Effect sein müsste.

Bevor du einen useEffect schreibst, lohnt sich die Frage: Kann ich diesen Wert direkt aus den Daten berechnen, die ich schon habe? Falls ja, kein Effect nötig. Falls du wirklich mit etwas außerhalb von React kommunizieren musst – API, Browser-API, Timer – dann ist useEffect genau richtig.


Weiterführende Artikel zu TypeScript in React und zum Aufbau eines kompletten Next.js-Projekts sind in Vorbereitung und werden hier verlinkt, sobald sie veröffentlicht sind.

FAQ

FAQ zu diesem Artikel

Über den Autor

d6benjamin

Willkommen auf d6b

Weiterlesen

Verwandte Artikel