Reaktive Daten — useState
useState ist der wichtigste Hook in React. In diesem Kapitel wirst du den Mechanismus der Zustandsverwaltung von React verstehen und die Kategorienfilterung implementieren.
Wie unterscheidet sich Reacts "Reaktivität" von Vue3?
Vue3 verwendet ref/reactive, um reaktive Daten zu erstellen; wenn sich die Daten ändern, aktualisiert sich die Ansicht automatisch.
Reacts Ansatz ist anders:Wenn du den Zustand änderst, führt React die Komponentenfunktion mit dem neuen Zustand erneut aus und rendert eine neue Ansicht。
Dies ist der Ursprung von Reacts Namen — Komponenten "reagieren" auf Änderungen des Zustands.
Grundlegende Verwendung von useState
useStateEs ist der grundlegendste Hook von React und wird verwendet, um Funktionskomponenten State hinzuzufügen.
Beispiel
import { useState } from 'react'
function Counter() {
// useState(Anfangswert) gibt ein Array zurück: [aktueller Wert, Aktualisierungsfunktion]
const [count, setCount] = useState(0)
function handleClick() {
setCount(count + 1) // State aktualisieren, React rendert die Komponente automatisch neu
}
return (
<div>
<p>Klickzähler:{count}</p>
<button onClick={handleClick}>+1</button>
</div>
)
}
Jedes Mal, wenn der Button geklickt wird,setCountder Wert von count wird aktualisiert, React führt die Funktion Counter mit dem neuen Wert erneut aus, gibt neues JSX zurück, und die Seite wird automatisch aktualisiert.
React-Konvention: State-Namen verwenden
[something, setSomething]Destrukturierung. Funktionen, die mit set beginnen, sind eine übliche Namenskonvention, keine zwingende Voraussetzung, aber die gesamte Community schreibt es so.
Warum können wir den State nicht direkt ändern?
In React istState schreibgeschützt. Er muss über eine Setter-Funktion aktualisiert werden; du kannst ihn nicht direkt ändern.
Beispiel
function ArticleManager() {
const [title, setTitle] = useState('Default Title')
// Falscher Weg: direkte Änderung
// title = 'New Title' // React weiß nicht, dass du ihn geändert hast, also wird nicht neu gerendert
// Richtiger Weg: Aktualisierung über Setter
function updateTitle() {
setTitle('New Title') // React weiß, dass sich der State geändert hat, und rendert neu
}
return (
<div>
<p>{title}</p>
<button onClick={updateTitle}>Titel aktualisieren</button>
</div>
)
}
Dahinter steht ReactsImmutable-Prinzip: Alte Daten nicht verändern, sondern durch neue ersetzen.
Die richtige Vorgehensweise zum Aktualisieren von Arrays und Objekten
Bei Arrays und Objekten kann man nicht direkt push, splice oder Eigenschaften ändern; man muss eine neue Kopie erstellen.
Beispiel
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'React lernen', done: false },
{ id: 2, text: 'Blog schreiben', done: wahr }
])
// Ein Element hinzufügen: das alte Array spreaden + neues Element
function addTodo(text) {
const newTodo = { id: Date.now(), text, done: false }
setTodos([...todos, newTodo]) // Verwenden Sie den Spread-Operator, um ein neues Array zu erstellen
}
// Ein Element ändern: mit map iterieren, das zu ändernde finden, ein neues Objekt zurückgeben
function toggleTodo(id) {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
))
}
// Ein Element löschen: filter verwenden, um das unerwünschte Element zu entfernen
function removeTodo(id) {
setTodos(todos.filter(todo => todo.id !== id))
}
return (
<ul>
{todos.map(todo => (
<li key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.done ? <s>{todo.text}</s> : todo.text}
</li>
))}
</ul>
)
}
| Aktion | Falscher Weg | Richtiger Weg |
|---|---|---|
| Add | arr.push(item) | setArr([...arr, item]) |
| Ändern | arr[0] = newVal | setArr(arr.map(el => el.id === id ? ... : el)) |
| Delete | arr.splice(i, 1) | setArr(arr.filter(el => el.id !== id)) |
| Objekteigenschaften ändern | obj.name = 'xxx' | setObj({ ...obj, name: 'xxx' }) |
Kernprinzip: Ersetze immer das Alte durch ein neues Array/neues Objekt. Der Spread-Operator
...und die Array-Methoden, die du gelernt hast (map, filter), sind die besten Partner für unveränderliche Updates.
useMemo — Abgeleitete berechnete Werte
useMemoDient zum Zwischenspeichern von Berechnungsergebnissen. Wenn sich die Abhängigkeiten nicht geändert haben, wird die Neuberechnung übersprungen, um unnötigen Performance-Overhead zu vermeiden.
Beispiel
function ArticleFilter() {
const [articles] = useState([
{ id: 1, title: 'Getting Started with React', category: 'React' },
{ id: 2, title: 'Promise Explained', category: 'JavaScript' },
{ id: 3, title: 'Grid Layout', category: 'CSS' },
{ id: 4, title: 'React Hooks', category: 'React' }
])
const [activeCategory, setActiveCategory] = useState('Alle')
// useMemo speichert das Filterergebnis zwischen; es berechnet nur neu, wenn sich Abhängigkeiten ändern
const filteredArticles = useMemo(() => {
console.log('Filterergebnisse neu berechnen') // Cache-Effekt überprüfen
if (activeCategory === 'All') return articles
return articles.filter(a => a.category === activeCategory)
}, [articles, activeCategory]) // Abhängigkeitsarray
// useMemo verwenden, um Statistiken zu berechnen
const totalCount = useMemo(() => articles.length, [articles])
const filteredCount = useMemo(() => filteredArticles.length, [filteredArticles])
return (
<div>
<p>common{totalCount}Artikel, aktuell{filteredCount}Artikel</p>
<button onClick={() => setActiveCategory('Alle')}>Alle</button>
<button onClick={() => setActiveCategory('React')}>React</button>
<button onClick={() => setActiveCategory('JavaScript')}>JavaScript</button>
<button onClick={() => setActiveCategory('CSS')}>CSS</button>
{filteredArticles.map(a => (
<div key={a.id}><h3>{a.title}</h3><span>{a.category}</span></div>
))}
</div>
)
}
Das Dependency-Array von useMemo(fn, deps) muss vollständig angegeben werden: Alle internen Variablen der Komponente, die in der Funktion verwendet werden, sollten im Array deklariert werden. React entscheidet anhand eines flachen Vergleichs, ob neu berechnet wird.
Praktische Übung: Kategorienfilterung zum Blog hinzufügen
Beispiel
import { useState, useMemo } from 'react'
import './App.css'
function App() {
const [articles] = useState([
{ id: 1, title: 'Der vollständige Leitfaden für den Einstieg mit React', summary: 'React von Grund auf lernen', category: 'React', date: '2024-05-10' },
{ id: 2, title: 'Asynchrone Programmierung in JS erklärt', summary: 'Promise und async/await verstehen', category: 'JavaScript', date: '2024-05-08' },
{ id: 3, title: 'CSS Grid Layout in Aktion', summary: 'Responsive Layouts mit Grid implementieren', category: 'CSS', date: '2024-05-05' },
{ id: 4, title: 'React Hooks im Detail', summary: 'Tiefgehendes Verständnis von useState und useEffect', category: 'React', date: '2024-05-03' },
{ id: 5, title: 'Der vollständige Leitfaden zu Flexbox', summary: 'Flex Layout in einem Artikel lernen', category: 'CSS', date: '2024-05-01' },
])
const [activeCategory, setActiveCategory] = useState('Alle')
// Alle Kategorien extrahieren (Duplikate entfernen)
const categories = useMemo(() => {
const cats = articles.map(a => a.category)
return ['All', ...new Set(cats)]
}, [articles])
// Artikel nach Kategorie filtern
const filteredArticles = useMemo(() => {
if (activeCategory === 'All') return articles
return articles.filter(a => a.category === activeCategory)
}, [articles, activeCategory])
return (
<div className="app">
<header className="navbar">
<h1 className="logo">BEISPIEL Blog</h1>
<nav>
<a href="/">Startseite</a>
<a href="#">Über</a>
</nav>
</header>
<main className="container">
<h2 className="section-title">Neueste Artikel</h2>
{/* Kategorie-Filter-Schaltflächengruppe */}
<div className="category-bar">
{categories.map(cat => (
<button
key={cat}
className={activeCategory === cat ? 'active' : ''}
onClick={() => setActiveCategory(cat)}
>
{cat}
</button>
))}
</div>
<p className="result-info">common{filteredArticles.length}Artikel</p>
{filteredArticles.length === 0 ? (
<p className="empty-tip">In dieser Kategorie gibt es noch keine Artikel</p>
) : (
<div className="article-grid">
{filteredArticles.map(article => (
<div key={article.id} className="article-card">
<div className="card-content">
<span className="card-category">{article.category}</span>
<h3>{article.title}</h3>
<p>{article.summary}</p>
<span className="card-date">{article.date}</span>
</div>
</div>
))}
</div>
)}
</main>
<footer className="footer">
<p>© 2024 BEISPIEL Blog. Betrieben mit React.</p>
</footer>
</div>
)
}
export default App
Beispiel
.category-bar {
display: flex;
gap: 10px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.category-bar button {
padding: 6px 16px;
border: 1px solid #ddd;
border-radius: 20px;
background: #fff;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.category-bar button.active {
background: #1976d2;
color: #fff;
border-color: #1976d2;
}
.category-bar button:hover {
border-color: #1976d2;
}
.result-info {
color: #999;
font-size: 14px;
margin-bottom: 16px;
}
Kapitelzusammenfassung
In diesem Kapitel haben Sie die drei Kernkonzepte des React-State-Managements gemeistert: useState zum Erstellen und Aktualisieren von State, das Prinzip der unveränderlichen Aktualisierung (Spread-Operator + map/filter) und useMemo zum Zwischenspeichern abgeleiteter berechneter Werte.
Durch die Funktion zur Kategoriefilterung haben Sie das datengesteuerte Muster von React "setState → Re-Render → neue Ansicht" kennengelernt.
Andere Erweiterungen