Fortgeschritten202025-01-15

React Router - Navigation in React Apps

Lerne React Router kennen und baue Multi-Page Apps mit Navigation, Dynamic Routes, URL Parameters und Protected Routes!

#react#react-router#routing#navigation#spa

React Router

React Router ist die Standard-Routing-Library für React. Sie ermöglicht Navigation zwischen verschiedenen Views/Pages in deiner Single-Page-Application (SPA).

Installation

React Router installieren
# NPM
npm install react-router-dom

# Yarn
yarn add react-router-dom

Basic Setup

Router Setup
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Contact from './pages/Contact'

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  )
}

export default App

Navigation - Links

Link Component
import { Link } from 'react-router-dom'

function Navbar() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/contact">Contact</Link>
    </nav>
  )
}

// ⚠️ NICHT <a href="/"> verwenden!
// Das würde die Seite neu laden

NavLink - Active Styling

NavLink für aktive Links
import { NavLink } from 'react-router-dom'

function Navbar() {
  return (
    <nav>
      <NavLink
        to="/"
        className={({ isActive }) =>
          isActive ? 'nav-link active' : 'nav-link'
        }
      >
        Home
      </NavLink>

      <NavLink
        to="/about"
        style={({ isActive }) => ({
          color: isActive ? 'red' : 'blue'
        })}
      >
        About
      </NavLink>
    </nav>
  )
}

Dynamic Routes - URL Parameters

Dynamic Routes mit Parametern
import { Routes, Route, useParams } from 'react-router-dom'

function App() {
  return (
    <Routes>
      <Route path="/users/:userId" element={<UserProfile />} />
      <Route path="/posts/:postId" element={<PostDetail />} />
      <Route path="/blog/:category/:slug" element={<BlogPost />} />
    </Routes>
  )
}

// UserProfile Component
function UserProfile() {
  const { userId } = useParams()

  return <h1>User Profile: {userId}</h1>
}

// Multi-Parameter
function BlogPost() {
  const { category, slug } = useParams()

  return (
    <div>
      <h1>Category: {category}</h1>
      <h2>Slug: {slug}</h2>
    </div>
  )
}

// URL: /blog/react/hooks
// category = 'react'
// slug = 'hooks'

Programmatic Navigation

useNavigate Hook
import { useNavigate } from 'react-router-dom'

function LoginPage() {
  const navigate = useNavigate()

  const handleLogin = async (credentials) => {
    const success = await login(credentials)
    if (success) {
      // Nach Login zum Dashboard
      navigate('/dashboard')
    }
  }

  return (
    <form onSubmit={handleLogin}>
      <button type="submit">Login</button>
    </form>
  )
}

// Navigation mit State
function ProductList() {
  const navigate = useNavigate()

  const viewProduct = (product) => {
    navigate('/product/' + product.id, {
      state: { product }
    })
  }
}

// Zurück navigieren
function BackButton() {
  const navigate = useNavigate()
  return <button onClick={() => navigate(-1)}>Zurück</button>
}

// Ersetzen statt Pushen
navigate('/home', { replace: true })

Nested Routes

Verschachtelte Routes
import { Routes, Route, Outlet } from 'react-router-dom'

function App() {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="dashboard" element={<Dashboard />}>
          <Route index element={<DashboardHome />} />
          <Route path="profile" element={<Profile />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Route>
    </Routes>
  )
}

// Layout Component
function Layout() {
  return (
    <div>
      <Navbar />
      <main>
        <Outlet /> {/* Child Routes rendern hier */}
      </main>
      <Footer />
    </div>
  )
}

// Dashboard Component
function Dashboard() {
  return (
    <div>
      <DashboardSidebar />
      <div>
        <Outlet /> {/* Nested Routes */}
      </div>
    </div>
  )
}

// URLs:
// /                    -> Layout + Home
// /about               -> Layout + About
// /dashboard           -> Layout + Dashboard + DashboardHome
// /dashboard/profile   -> Layout + Dashboard + Profile
// /dashboard/settings  -> Layout + Dashboard + Settings

Protected Routes

Route Protection
import { Navigate } from 'react-router-dom'
import { useAuth } from './contexts/AuthContext'

// Protected Route Component
function ProtectedRoute({ children }) {
  const { isAuthenticated, loading } = useAuth()

  if (loading) {
    return <div>Loading...</div>
  }

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />
  }

  return children
}

// In App verwenden
function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />

      {/* Protected Routes */}
      <Route
        path="/dashboard"
        element={
          <ProtectedRoute>
            <Dashboard />
          </ProtectedRoute>
        }
      />

      <Route
        path="/profile"
        element={
          <ProtectedRoute>
            <Profile />
          </ProtectedRoute>
        }
      />
    </Routes>
  )
}

// Oder mit Outlet
function ProtectedLayout() {
  const { isAuthenticated } = useAuth()

  if (!isAuthenticated) {
    return <Navigate to="/login" />
  }

  return (
    <div>
      <Navbar />
      <Outlet />
    </div>
  )
}

function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />
      <Route element={<ProtectedLayout />}>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile" element={<Profile />} />
        <Route path="/settings" element={<Settings />} />
      </Route>
    </Routes>
  )
}

Query Parameters

useSearchParams Hook
import { useSearchParams } from 'react-router-dom'

function ProductList() {
  const [searchParams, setSearchParams] = useSearchParams()

  // Query Parameters lesen
  const category = searchParams.get('category')
  const sort = searchParams.get('sort')
  const page = searchParams.get('page') || '1'

  // Query Parameters setzen
  const filterByCategory = (cat) => {
    setSearchParams({ category: cat, sort, page })
  }

  const nextPage = () => {
    setSearchParams({
      category,
      sort,
      page: String(Number(page) + 1)
    })
  }

  return (
    <div>
      <h1>Category: {category}</h1>
      <button onClick={() => filterByCategory('electronics')}>
        Electronics
      </button>
      <button onClick={nextPage}>Next Page</button>
    </div>
  )
}

// URL: /products?category=electronics&sort=price&page=2
// category = 'electronics'
// sort = 'price'
// page = '2'

404 - Not Found

404 Page
function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />

      {/* Catch-all Route für 404 */}
      <Route path="*" element={<NotFound />} />
    </Routes>
  )
}

function NotFound() {
  const navigate = useNavigate()

  return (
    <div>
      <h1>404 - Seite nicht gefunden</h1>
      <p>Die angeforderte Seite existiert nicht.</p>
      <button onClick={() => navigate('/')}>
        Zurück zur Startseite
      </button>
    </div>
  )
}

useLocation Hook

Aktuelle Location-Daten
import { useLocation } from 'react-router-dom'

function CurrentPath() {
  const location = useLocation()

  console.log(location.pathname)  // '/dashboard/profile'
  console.log(location.search)    // '?tab=settings'
  console.log(location.hash)      // '#section-2'
  console.log(location.state)     // { from: '/login' }

  return (
    <div>
      <p>Current Path: {location.pathname}</p>
    </div>
  )
}

// State übergeben
function LoginPage() {
  const navigate = useNavigate()
  const location = useLocation()

  const from = location.state?.from || '/'

  const handleLogin = async () => {
    // Nach Login zurück zur ursprünglichen Seite
    navigate(from, { replace: true })
  }
}

Lazy Loading - Code Splitting

Lazy Loading von Routes
import { lazy, Suspense } from 'react'
import { Routes, Route } from 'react-router-dom'

// Lazy Load Components
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
const Dashboard = lazy(() => import('./pages/Dashboard'))

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Suspense>
  )
}

// Oder eigene Loading Component
function LoadingSpinner() {
  return (
    <div className="loading-spinner">
      <div className="spinner"></div>
      <p>Loading page...</p>
    </div>
  )
}

<Suspense fallback={<LoadingSpinner />}>
  <Routes>...</Routes>
</Suspense>

Complete App Example

Komplette App mit Routing
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  Navigate
} from 'react-router-dom'
import { useAuth } from './contexts/AuthContext'

// Layout
function Layout() {
  return (
    <div>
      <Navbar />
      <main>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/blog" element={<Blog />} />
          <Route path="/blog/:slug" element={<BlogPost />} />
          <Route path="/login" element={<Login />} />

          {/* Protected Routes */}
          <Route element={<ProtectedLayout />}>
            <Route path="/dashboard" element={<Dashboard />} />
            <Route path="/profile" element={<Profile />} />
            <Route path="/settings" element={<Settings />} />
          </Route>

          {/* 404 */}
          <Route path="*" element={<NotFound />} />
        </Routes>
      </main>
      <Footer />
    </div>
  )
}

function Navbar() {
  const { isAuthenticated, logout } = useAuth()

  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
      <Link to="/blog">Blog</Link>

      {isAuthenticated ? (
        <>
          <Link to="/dashboard">Dashboard</Link>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <Link to="/login">Login</Link>
      )}
    </nav>
  )
}

function ProtectedLayout() {
  const { isAuthenticated } = useAuth()

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />
  }

  return <Outlet />
}

function App() {
  return (
    <BrowserRouter>
      <Layout />
    </BrowserRouter>
  )
}

export default App

📝 Quiz

Was ist der Unterschied zwischen Link und a-Tag?

Tipps & Tricks

Base URL konfigurieren

// Wenn App nicht in Root läuft
<BrowserRouter basename="/my-app">
  <Routes>...</Routes>
</BrowserRouter>

// URL: example.com/my-app/about

Redirect Component

import { Navigate } from 'react-router-dom'

// Alte Route zu neuer umleiten
<Route path="/old-page" element={<Navigate to="/new-page" replace />} />

// Conditional Redirect
function Home() {
  const { isAuthenticated } = useAuth()

  if (isAuthenticated) {
    return <Navigate to="/dashboard" />
  }

  return <h1>Welcome!</h1>
}

Active Link Styling

// Mit className
<NavLink
  to="/about"
  className={({ isActive }) => isActive ? 'active' : ''}
>
  About
</NavLink>

// CSS
.active {
  color: blue;
  font-weight: bold;
  border-bottom: 2px solid blue;
}

Scroll to Top

import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'

function ScrollToTop() {
  const { pathname } = useLocation()

  useEffect(() => {
    window.scrollTo(0, 0)
  }, [pathname])

  return null
}

// In App
<BrowserRouter>
  <ScrollToTop />
  <Routes>...</Routes>
</BrowserRouter>

Häufige Fehler

Fehler 1: a-Tag statt Link

FALSCH:

<a href="/about">About</a>
// Lädt Seite neu! ❌

RICHTIG:

<Link to="/about">About</Link>
// Client-Side Routing ✅

Fehler 2: Router vergessen

FALSCH:

function App() {
  return (
    <Routes>  {/* Error! */}
      <Route path="/" element={<Home />} />
    </Routes>
  )
}

RICHTIG:

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
      </Routes>
    </BrowserRouter>
  )
}

Fehler 3: Falsche Pfade

FALSCH:

<Route path="about" element={<About />} />
<Link to="about">About</Link>
// Relative Pfade können problematisch sein

RICHTIG:

<Route path="/about" element={<About />} />
<Link to="/about">About</Link>
// Absolute Pfade sind eindeutig

Fehler 4: useNavigate in Render

FALSCH:

function Component() {
  const navigate = useNavigate()
  navigate('/home') // Infinite Loop!
  return <div>Content</div>
}

RICHTIG:

function Component() {
  const navigate = useNavigate()

  useEffect(() => {
    navigate('/home')
  }, [])

  // Oder in Event Handler
  const handleClick = () => {
    navigate('/home')
  }
}
🎯

Zusammenfassung

Du hast gelernt:

  • ✅ React Router für Navigation
  • ✅ BrowserRouter + Routes + Route
  • ✅ Link und NavLink Components
  • ✅ Dynamic Routes mit useParams
  • ✅ Programmatic Navigation mit useNavigate
  • ✅ Nested Routes mit Outlet
  • ✅ Protected Routes implementieren
  • ✅ Query Parameters mit useSearchParams
  • ✅ 404 Pages mit Wildcard Route

Key Takeaways:

  • BrowserRouter um gesamte App wrappen
  • Link statt a-Tag verwenden
  • NavLink für aktive Links
  • useParams für URL-Parameter
  • useNavigate für programmatische Navigation
  • Protected Routes mit Navigate
  • Lazy Loading für bessere Performance

Route Types:

  • Static: /about
  • Dynamic: /users/:id
  • Nested: /dashboard/profile
  • Wildcard: * für 404
  • Index: index für Default Route

Best Practices:

  • Absolute Pfade verwenden (/about)
  • Protected Routes für Auth
  • 404 Page implementieren
  • Lazy Loading für große Apps
  • ScrollToTop Component

Praktische Übungen

Übung 1: Basic Routing

Erstelle eine App mit:

  • Home Page
  • About Page
  • Contact Page
  • Navbar mit Links

Übung 2: Blog mit Dynamic Routes

Implementiere:

  • /blog - Liste aller Posts
  • /blog/:slug - Einzelner Post
  • useParams für Slug

Übung 3: Dashboard mit Nested Routes

Baue ein Dashboard:

  • /dashboard - Overview
  • /dashboard/profile - User Profile
  • /dashboard/settings - Settings
  • Sidebar Navigation

Übung 4: Protected Routes

Implementiere:

  • Login Page
  • Protected Dashboard
  • Redirect zu Login wenn nicht eingeloggt
  • Nach Login redirect zu Dashboard

Übung 5: E-Commerce Routing

Erstelle:

  • /products - Product List
  • /products/:id - Product Detail
  • /cart - Shopping Cart
  • /checkout - Checkout (protected)
  • Query Params für Filter/Sort
ReactLektion 8 von 10
80% abgeschlossen

Artikel bewerten

0.0 (0 Bewertungen)

Bitte einloggen um zu bewerten