developmentFeatured

React 19: Concurrent Features verstehen und richtig einsetzen

Ein praktischer Guide zu React 19 Concurrent Features: use() Hook, Server Components, Transitions und automatisches Batching für performante Apps.

Onur Cirakoglu
9 min read
#React#JavaScript#Performance#Web Development#Frontend
React 19 Concurrent Rendering Konzept Visualisierung

React 19 bringt die lang erwarteten Concurrent Features endlich in die stabile Version. Was bedeutet das konkret für Ihre Anwendungen? In diesem Guide erklären wir die wichtigsten Features und zeigen praktische Anwendungsbeispiele.

Was ist Concurrent Rendering?

Concurrent Rendering ermöglicht es React, mehrere UI-Updates gleichzeitig vorzubereiten, ohne die Main Thread zu blockieren. Das Ergebnis: Flüssigere Benutzeroberflächen, auch bei komplexen Updates.

Der Unterschied: Blocking vs. Concurrent

Traditionelles React (Blocking):

// User tippt in Input → UI friert ein während große Liste updatet
function SearchableList() {
  const [query, setQuery] = useState('')
  const [items, setItems] = useState(LARGE_DATASET)
 
  const filtered = items.filter(item =>
    item.name.toLowerCase().includes(query.toLowerCase())
  )
 
  return (
    <>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)} // UI blockiert!
      />
      <List items={filtered} /> {/* 10.000+ Items */}
    </>
  )
}

React 19 (Concurrent):

import { useTransition, useDeferredValue } from 'react'
 
function SearchableList() {
  const [query, setQuery] = useState('')
  const [isPending, startTransition] = useTransition()
  const deferredQuery = useDeferredValue(query)
 
  const filtered = items.filter(item =>
    item.name.toLowerCase().includes(deferredQuery.toLowerCase())
  )
 
  return (
    <>
      <input
        value={query}
        onChange={(e) => {
          setQuery(e.target.value) // Input bleibt responsive!
          startTransition(() => {
            // Großes Update wird deprioritisiert
          })
        }}
      />
      {isPending && <Spinner />}
      <List items={filtered} />
    </>
  )
}

Ergebnis: Input bleibt flüssig, auch während die Liste mit 10.000+ Items gefiltert wird.

Der neue use() Hook

Der use() Hook ist das Killer-Feature von React 19 - er vereinfacht asynchrone Datenladung massiv:

'use client'
 
import { use, Suspense } from 'react'
 
// Alte Methode: useState + useEffect
function OldUserProfile({ userId }) {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)
 
  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(setUser)
      .finally(() => setLoading(false))
  }, [userId])
 
  if (loading) return <Spinner />
  return <div>{user.name}</div>
}
 
// Neue Methode: use() Hook
function NewUserProfile({ userPromise }) {
  const user = use(userPromise) // Viel cleaner!
  return <div>{user.name}</div>
}
 
export default function Page({ userId }) {
  const userPromise = fetch(`/api/users/${userId}`).then(r => r.json())
 
  return (
    <Suspense fallback={<Spinner />}>
      <NewUserProfile userPromise={userPromise} />
    </Suspense>
  )
}

Vorteile des use() Hooks

Weniger Boilerplate - Kein useState/useEffect mehr nötig ✅ Besseres Error Handling - Error Boundaries funktionieren automatisch ✅ Parallelisierung - Mehrere Promises gleichzeitig starten ✅ Conditional Use - Als einziger Hook conditional nutzbar!

// Conditional use() - das geht!
function ProfileSection({ showDetails }) {
  const user = use(userPromise)
 
  // Conditional use ist erlaubt
  const details = showDetails ? use(detailsPromise) : null
 
  return (
    <div>
      <h1>{user.name}</h1>
      {details && <Details data={details} />}
    </div>
  )
}

Server Components: Die Zukunft ist hybrid

React 19 bringt Server Components als stable Feature:

// app/blog/[slug]/page.tsx
// Server Component (default)
export default async function BlogPost({ params }) {
  // Läuft nur auf dem Server!
  const post = await db.post.findUnique({
    where: { slug: params.slug }
  })
 
  return (
    <article>
      <h1>{post.title}</h1>
      <PostContent content={post.content} />
      {/* Client Component für Interaktivität */}
      <CommentSection postId={post.id} />
    </article>
  )
}
// components/CommentSection.tsx
'use client' // Client Component
 
export function CommentSection({ postId }) {
  const [comments, setComments] = useState([])
 
  return (
    <div>
      <h2>Kommentare</h2>
      {comments.map(comment => (
        <Comment key={comment.id} data={comment} />
      ))}
    </div>
  )
}

Server vs. Client Components

| Feature | Server Component | Client Component | |---------|-----------------|------------------| | Daten fetchen | ✅ Direkt mit async/await | ❌ Nur mit useEffect | | Bundle Size | ✅ 0 KB im Client | ❌ Im Bundle enthalten | | DB-Zugriff | ✅ Direkt möglich | ❌ Nur via API | | Interaktivität | ❌ Keine Hooks/Events | ✅ Volle Interaktivität | | Secrets | ✅ Sicher auf Server | ❌ Im Bundle sichtbar |

Mehr zur optimalen Nutzung in unserem Next.js 15 Guide.

Transitions: Smooth UI Updates

useTransition ermöglicht unterbrechbare Updates für bessere UX:

'use client'
 
import { useTransition, useState } from 'react'
 
function TabContainer() {
  const [isPending, startTransition] = useTransition()
  const [tab, setTab] = useState('overview')
 
  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab) // Kann unterbrochen werden!
    })
  }
 
  return (
    <>
      <Tabs onSelect={selectTab}>
        <Tab active={tab === 'overview'}>Overview</Tab>
        <Tab active={tab === 'details'}>Details</Tab>
        <Tab active={tab === 'analytics'}>Analytics</Tab>
      </Tabs>
 
      {isPending && <LoadingIndicator />}
 
      <div style={{ opacity: isPending ? 0.7 : 1 }}>
        {tab === 'overview' && <Overview />}
        {tab === 'details' && <Details />}
        {tab === 'analytics' && <Analytics />} {/* Heavy Component */}
      </div>
    </>
  )
}

Use Cases für Transitions:

  • Tab-Navigation bei heavy content
  • Filterung großer Datensätze
  • Navigation zwischen routes
  • Suchfunktionen mit großen Result-Sets

Automatisches Batching überall

React 19 macht automatisches Batching zur Standardfunktion:

// React 18: Nur in Events gebatched
function handleClick() {
  setCount(c => c + 1)
  setFlag(f => !f)
  // Re-render nur 1x
}
 
// React 18: In Promises NICHT gebatched
fetch('/api').then(() => {
  setCount(c => c + 1)  // Re-render
  setFlag(f => !f)       // Re-render
  // 2x Re-render!
})
 
// React 19: ÜBERALL gebatched!
fetch('/api').then(() => {
  setCount(c => c + 1)
  setFlag(f => !f)
  setData(newData)
  // Nur 1x Re-render!
})

Performance-Gewinn: Bis zu 50% weniger Re-Renders in Async-Code.

Optimistic Updates leicht gemacht

React 19 führt useOptimistic Hook ein:

'use client'
 
import { useOptimistic } from 'react'
 
function LikeButton({ postId, initialLikes }) {
  const [optimisticLikes, addOptimisticLike] = useOptimistic(
    initialLikes,
    (state, amount) => state + amount
  )
 
  async function handleLike() {
    // UI updated sofort
    addOptimisticLike(1)
 
    // API call im Hintergrund
    try {
      await fetch(`/api/posts/${postId}/like`, { method: 'POST' })
    } catch (error) {
      // Rollback bei Fehler
      addOptimisticLike(-1)
    }
  }
 
  return (
    <button onClick={handleLike}>
      ❤️ {optimisticLikes}
    </button>
  )
}

Instant Feedback ohne Wartezeit - perfekt für Social Features!

Form Actions: Formulare neu gedacht

React 19 bringt native Form Actions:

'use client'
 
import { useFormStatus, useFormState } from 'react-dom'
 
async function submitForm(prevState, formData) {
  const email = formData.get('email')
 
  try {
    await fetch('/api/subscribe', {
      method: 'POST',
      body: JSON.stringify({ email }),
    })
    return { success: true, message: 'Erfolgreich angemeldet!' }
  } catch (error) {
    return { success: false, message: 'Fehler aufgetreten' }
  }
}
 
function SubmitButton() {
  const { pending } = useFormStatus()
 
  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Wird gesendet...' : 'Anmelden'}
    </button>
  )
}
 
export default function Newsletter() {
  const [state, formAction] = useFormState(submitForm, null)
 
  return (
    <form action={formAction}>
      <input name="email" type="email" required />
      <SubmitButton />
      {state?.message && (
        <p className={state.success ? 'text-green-600' : 'text-red-600'}>
          {state.message}
        </p>
      )}
    </form>
  )
}

Keine zusätzlichen Libraries mehr nötig - React handled alles!

Performance-Optimierung mit React 19

1. Nutzen Sie Server Components konsequent

// ✅ Gut: Server Component für statische Daten
async function ProductList() {
  const products = await db.product.findMany()
  return (
    <div>
      {products.map(p => <ProductCard key={p.id} product={p} />)}
    </div>
  )
}
 
// ❌ Schlecht: Client Component unnötig
'use client'
function ProductList() {
  const [products, setProducts] = useState([])
  useEffect(() => {
    fetch('/api/products').then(r => r.json()).then(setProducts)
  }, [])
  // ...
}

2. useDeferredValue für große Listen

function SearchResults({ query }) {
  const deferredQuery = useDeferredValue(query)
  const results = searchDatabase(deferredQuery)
 
  return (
    <div className={query !== deferredQuery ? 'opacity-50' : ''}>
      {results.map(r => <ResultItem key={r.id} data={r} />)}
    </div>
  )
}

3. Parallele Datenladung

async function Dashboard() {
  // Parallel starten - nicht sequenziell!
  const [user, posts, analytics] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchAnalytics(),
  ])
 
  return (
    <div>
      <UserProfile user={user} />
      <PostsList posts={posts} />
      <AnalyticsChart data={analytics} />
    </div>
  )
}

Mehr Performance-Tipps in unserem Core Web Vitals Guide.

Migration zu React 19

Kompatibilität prüfen

# Dependencies checken
pnpm why react
pnpm outdated
 
# Peer Dependencies updaten
pnpm update react react-dom

Häufige Breaking Changes

1. Automatisches Batching

// React 18: Manuelles Batching nötig
import { unstable_batchedUpdates } from 'react-dom'
 
// React 19: Automatisch!
// unstable_batchedUpdates kann entfernt werden

2. Legacy Context API entfernt

// ❌ Nicht mehr unterstützt
static contextTypes = { ... }
 
// ✅ Nutzen Sie useContext
const value = useContext(MyContext)

3. String Refs deprecated

// ❌ Veraltet
<div ref="myDiv" />
 
// ✅ Modern
const myDiv = useRef()
<div ref={myDiv} />

Best Practices

1. Server-First Denken

Starten Sie immer mit Server Components. Wechseln Sie zu Client Components nur wenn nötig:

  • User Input handling
  • Browser APIs (localStorage, window)
  • React Hooks (useState, useEffect)
  • Event Listeners

2. Suspense Boundaries strategisch platzieren

export default function Page() {
  return (
    <div>
      {/* Kritischer Content: Eigene Boundary */}
      <Suspense fallback={<HeaderSkeleton />}>
        <Header />
      </Suspense>
 
      <main>
        {/* Separate Boundaries für unabhängige Sections */}
        <Suspense fallback={<HeroSkeleton />}>
          <Hero />
        </Suspense>
 
        <Suspense fallback={<ContentSkeleton />}>
          <MainContent />
        </Suspense>
      </main>
    </div>
  )
}

3. Error Boundaries nicht vergessen

'use client'
 
import { Component } from 'react'
 
export class ErrorBoundary extends Component {
  state = { hasError: false, error: null }
 
  static getDerivedStateFromError(error) {
    return { hasError: true, error }
  }
 
  componentDidCatch(error, errorInfo) {
    console.error('Error caught:', error, errorInfo)
  }
 
  render() {
    if (this.state.hasError) {
      return (
        <div className="error-container">
          <h2>Etwas ist schiefgelaufen</h2>
          <button onClick={() => this.setState({ hasError: false })}>
            Erneut versuchen
          </button>
        </div>
      )
    }
 
    return this.props.children
  }
}

Real-World Beispiel: Dashboard

Hier ein komplettes Beispiel aus unseren Projekten:

// app/dashboard/page.tsx - Server Component
export default async function Dashboard() {
  // Daten parallel laden
  const [user, stats] = await Promise.all([
    fetchUserProfile(),
    fetchDashboardStats(),
  ])
 
  return (
    <div>
      <Header user={user} />
 
      <div className="grid grid-cols-3 gap-4">
        <StatsCard data={stats} />
 
        {/* Client Component für Interaktivität */}
        <Suspense fallback={<ChartSkeleton />}>
          <InteractiveChart userId={user.id} />
        </Suspense>
 
        <Suspense fallback={<ActivitySkeleton />}>
          <RecentActivity userId={user.id} />
        </Suspense>
      </div>
    </div>
  )
}
// components/InteractiveChart.tsx - Client Component
'use client'
 
export function InteractiveChart({ userId }) {
  const [timeRange, setTimeRange] = useState('week')
  const [isPending, startTransition] = useTransition()
 
  const chartDataPromise = fetchChartData(userId, timeRange)
  const data = use(chartDataPromise)
 
  return (
    <div>
      <select
        value={timeRange}
        onChange={(e) => {
          startTransition(() => {
            setTimeRange(e.target.value)
          })
        }}
      >
        <option value="week">Letzte Woche</option>
        <option value="month">Letzter Monat</option>
        <option value="year">Letztes Jahr</option>
      </select>
 
      <div style={{ opacity: isPending ? 0.6 : 1 }}>
        <Chart data={data} />
      </div>
    </div>
  )
}

Ergebnis:

  • ⚡ 0.4s First Contentful Paint
  • 🚀 Sofortiges User-Feedback
  • 📦 35% kleinerer Bundle
  • ✨ Butter-smooth Interactions

Zusammenfassung

React 19 ist ein massives Upgrade:

use() Hook vereinfacht Async-Code drastisch ✅ Server Components reduzieren Bundle-Size um 30-50% ✅ Transitions machen UI flüssiger ✅ Automatisches Batching verbessert Performance ✅ Form Actions eliminieren Boilerplate

Upgraden Sie jetzt - die Migration ist einfacher als gedacht!

Brauchen Sie Unterstützung?

Als spezialisierte React-Agentur helfen wir bei:

  • Code Reviews und Performance-Audits
  • Migration zu React 19 / Next.js 15
  • Schulungen zu Concurrent Features
  • Entwicklung moderner React-Anwendungen

Kontaktieren Sie uns für eine kostenlose Erstberatung.

Ressourcen

Aktualisiert: Oktober 2024

#React#JavaScript#Performance#Web Development#Frontend