React Hooks: Der useState Hook

Basics-Trilogie

02.07.2025

dereact
Gregor Wedlich
Gregor Wedlich
Life, the Universe and Everything.
Donate with: Alby

Inhaltsverzeichnis

    Okay, okay, ich weiß was ihr denkt. "Jetzt zeigt er uns useState? DEN Hook, mit dem JEDER normale Mensch anfängt?" Ja, genau den. Warum erst jetzt? Keine Ahnung, vielleicht wollte ich euch erst die coolen Sachen zeigen, bevor wir zu den "Basics" kommen. Ist wie beim Autofahren lernen - erst Drift, dann Einparken. Macht total Sinn, oder?

    Aber im Ernst: Nach useRef und useEffect seid ihr bereit für die Wahrheit. useState ist gar nicht so simpel wie alle tun. Klar, const [count, setCount] = useState(0) sieht harmlos aus. Aber wartet mal ab...

    Die Basics-Trilogie

    Willkommen zum großen Finale meiner verkehrt-herum "Basics-Trilogie"! Falls du dich fragst, was hier los ist:

    1. Der useRef Hook - Das Schweizer Taschenmesser
    2. Der useEffect Hook - Der Hook für Seiteneffekte
    3. Der useState Hook ← Du bist hier!

    Was ist useState eigentlich?

    useState ist Reacts Art, sich Dinge zu merken. Ohne useState wäre React wie Dory aus "Findet Nemo" - nach jedem Render hätte es alles vergessen. useState gibt deinen Komponenten ein Gedächtnis.

    Der große Unterschied zu einer normalen JavaScript-Variable? Wenn sich State ändert, sagt React: "Alles klar, ich mal das nochmal neu!" Das ist das Re-Rendering, von dem alle immer reden.

    Der "Ich kann gar nicht so einfach sein"-Zähler

    Fangen wir mit dem Klassiker an, aber mit ein paar Überraschungen:

    1import { useState } from "react"; 2import React from "react"; 3 4const NotSoEasyCounter: React.FC = () => { 5 const [count, setCount] = useState<number>(0); 6 7 // This runs on EVERY render 8 console.log("🎨 Component renders! Count is:", count); 9 10 const increaseThreeTimes = (): void => { 11 // Surprise! This does NOT do what you think! 12 setCount(count + 1); 13 setCount(count + 1); 14 setCount(count + 1); 15 console.log("After 3x setCount, count is still:", count); 16 }; 17 18 const increaseThreeTimesCorrectly = (): void => { 19 // THIS is the correct way - with functional updates 20 setCount((prev) => prev + 1); 21 setCount((prev) => prev + 1); 22 setCount((prev) => prev + 1); 23 console.log("After 3x setCount, count is still:", count); 24 // Spoiler: Even here, console.log still shows the old value! 25 }; 26 27 const asyncFun = (): void => { 28 setTimeout(() => { 29 // Trap: 'count' here is the value from 2 seconds ago! 30 setCount(count + 1); 31 console.log("In timeout, count was:", count); 32 }, 2000); 33 34 setTimeout(() => { 35 // This is better: 36 setCount((prev) => prev + 1); 37 }, 3000); 38 }; 39 40 return ( 41 <div style={{ padding: "20px" }}> 42 <h2>The "not so easy" Counter</h2> 43 <p style={{ fontSize: "24px" }}>Count: {count}</p> 44 45 <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}> 46 <button onClick={() => setCount(count + 1)}>+1 (Normal)</button> 47 48 <button onClick={increaseThreeTimes}>+3 (Or maybe not?)</button> 49 50 <button onClick={increaseThreeTimesCorrectly}> 51 +3 (This time for real!) 52 </button> 53 54 <button onClick={asyncFun}>+1 after 2s, +1 after 3s</button> 55 </div> 56 57 <p style={{ marginTop: "20px", fontSize: "12px", color: "#666" }}> 58 Check the console for surprises! 🎭 59 </p> 60 </div> 61 ); 62}; 63 64export default NotSoEasyCounter;

    Warum increaseThreeTimes nur um 1 erhöht:

    Was passiert hier? React "sammelt" State-Updates und führt sie gebündelt aus (Batching). Alle drei setCount(count + 1) sehen denselben count Wert (z.B. 0), also rechnen alle drei 0 + 1. React sieht dreimal "setze State auf 1" - nicht "erhöhe dreimal um 1". Das ist wie wenn du dreimal "Schreib 1 auf die Tafel" sagst - am Ende steht da immer noch nur eine 1.

    Warum increaseThreeTimesCorrectly funktioniert:

    Der Unterschied: Mit prev => prev + 1 sagst du React: "Nimm den AKTUELLEN Wert und erhöhe ihn". Jede Funktion bekommt den neuesten Zwischenstand: Die erste macht aus 0→1, die zweite aus 1→2, die dritte aus 2→3. Das ist wie eine Staffelübergabe - jeder Läufer übergibt an den nächsten.

    Die Closure-Falle bei asyncFun:

    Das Problem: JavaScript "friert" den Wert von count ein, wenn die Funktion erstellt wird. Nach 2 Sekunden verwendet setCount(count + 1) immer noch den alten eingefrorenen Wert. Mit prev => prev + 1 umgehst du das, weil React dir immer den aktuellen Wert gibt. Das ist wie ein Foto vs. Live-Video - das Foto zeigt immer denselben Moment, das Video den aktuellen Stand.

    Nochwas:

    Selbst bei increaseThreeTimesCorrectly zeigt console.log noch den alten Wert! Warum? State-Updates sind asynchron - der neue Wert ist erst beim nächsten Render da. Das ist keine Magie, sondern Reacts Art, Performance zu optimieren.

    Lazy Loading für deinen State

    Manchmal ist das Berechnen des initialen States aufwendig. useState hat dafür einen Trick:

    1import { useState } from 'react'; 2 3interface UserData { 4 id: string; 5 preferences: { 6 theme: 'light' | 'dark'; 7 language: string; 8 notifications: boolean; 9 }; 10 createdAt: string; 11} 12 13function LazyInitialStateDemo() { 14 const [renderCount, setRenderCount] = useState<number>(0); 15 16 // ❌ BAD: This calculation runs on EVERY render (but result is ignored) 17 const generateUserId = (): string => { 18 console.log('❌ Generating ID again (wasteful!)'); 19 return `user-${Math.random().toString(36).substring(2, 11)}`; 20 }; 21 22 const [wastefulId] = useState<string>(generateUserId()); 23 24 // ✅ GOOD: This only runs once 25 const [efficientId] = useState<string>(() => { 26 console.log('✅ Generating ID only once!'); 27 return `user-${Math.random().toString(36).substring(2, 11)}`; 28 }); 29 30 // Real world example: Loading user data 31 const [userData] = useState<UserData>(() => { 32 console.log('📦 Loading user data from localStorage...'); 33 34 const saved = localStorage.getItem('userData'); 35 if (saved) { 36 return JSON.parse(saved); 37 } 38 39 // Create default user data 40 return { 41 id: crypto.randomUUID(), 42 preferences: { 43 theme: 'light', 44 language: 'en', 45 notifications: true 46 }, 47 createdAt: new Date().toISOString() 48 }; 49 }); 50 51 return ( 52 <div style={{ padding: '20px', maxWidth: '500px' }}> 53 <h2>Lazy Initial State Demo</h2> 54 55 <div style={{ 56 marginBottom: '20px', 57 padding: '15px', 58 background: '#e3f2fd', 59 borderRadius: '8px' 60 }}> 61 <p><strong>Click the button and watch the console!</strong></p> 62 <p>The wasteful calculation runs every time 😱</p> 63 <p>The efficient one only ran once ✨</p> 64 </div> 65 66 <div style={{ marginBottom: '20px' }}> 67 <p>Wasteful ID: <code>{wastefulId}</code></p> 68 <p>Efficient ID: <code>{efficientId}</code></p> 69 <p>User Theme: {userData.preferences.theme}</p> 70 </div> 71 72 <button 73 onClick={() => setRenderCount(prev => prev + 1)} 74 style={{ 75 padding: '10px 20px', 76 fontSize: '16px', 77 cursor: 'pointer' 78 }} 79 > 80 Re-render Component (Count: {renderCount}) 81 </button> 82 </div> 83 ); 84} 85 86export default LazyInitialStateDemo;

    Ohne () => läuft deine Funktion bei JEDEM Render - auch wenn useState das Ergebnis wegwirft! Das ist wie wenn du jeden Tag zur Arbeit ein Sandwich machst, es aber immer wegwirfst und dein altes vom ersten Tag isst.

    Wann brauchst du Lazy Initial State?

    • localStorage/sessionStorage lesen
    • Große Objekte erstellen
    • IDs generieren
    • JSON parsen
    • Crypto-Operationen
    • Alles was "rechenintensiv" ist

    Merkregel: Wenn dein initialer State eine Funktion aufruft → nutze () =>!

    Objekte und Arrays - Die Referenz-Falle

    Hier wird's richtig spannend. JavaScript und seine Referenzen...

    1import { useState } from 'react'; 2 3interface Person { 4 name: string; 5 age: number; 6 hobbies: string[]; 7 contact?: { 8 email?: string; 9 phone?: string; 10 }; 11} 12 13function MutationTrapDemo() { 14 const [person, setPerson] = useState<Person>({ 15 name: 'Max', 16 age: 25, 17 hobbies: ['Coding', 'Gaming'] 18 }); 19 20 const [updateCount, setUpdateCount] = useState(0); 21 const [mutationCount, setMutationCount] = useState(0); 22 23 // ❌ WRONG - Direct mutation 24 const wrongWayToUpdateAge = () => { 25 person.age = person.age + 1; // Mutating the existing object! 26 setPerson(person); // Same reference = React doesn't see the change 27 setMutationCount(prev => prev + 1); 28 console.log('❌ Age mutated to:', person.age, 'but no re-render!'); 29 }; 30 31 // ✅ CORRECT - Create new object 32 const correctWayToUpdateAge = () => { 33 setPerson({ 34 ...person, 35 age: person.age + 1 36 }); 37 setUpdateCount(prev => prev + 1); 38 console.log('✅ Created new object with age:', person.age + 1); 39 }; 40 41 // Visual comparison 42 const mutationExample = () => { 43 const original = { value: 1 }; 44 const mutated = original; 45 mutated.value = 2; 46 47 return original === mutated; // true - same reference! 48 }; 49 50 const immutableExample = () => { 51 const original = { value: 1 }; 52 const updated = { ...original, value: 2 }; 53 54 return original === updated; // false - different reference! 55 }; 56 57 return ( 58 <div style={{ padding: '20px', maxWidth: '600px' }}> 59 <h2>Why Mutations Don't Trigger Re-renders</h2> 60 61 <div style={{ 62 padding: '15px', 63 background: '#e3f2fd', 64 borderRadius: '8px', 65 marginBottom: '20px' 66 }}> 67 <h3>The Problem:</h3> 68 <p>React uses <strong>Object.is()</strong> to check if state changed.</p> 69 <p>If the reference is the same, React thinks nothing changed!</p> 70 <code style={{ display: 'block', marginTop: '10px' }}> 71 Object.is(oldState, newState) // If true = no re-render 72 </code> 73 </div> 74 75 <div style={{ 76 padding: '20px', 77 background: '#f5f5f5', 78 borderRadius: '8px', 79 marginBottom: '20px' 80 }}> 81 <h3>Current State:</h3> 82 <p><strong>Name:</strong> {person.name}</p> 83 <p><strong>Age:</strong> {person.age}</p> 84 <p><strong>Hobbies:</strong> {person.hobbies.join(', ')}</p> 85 86 <div style={{ 87 marginTop: '15px', 88 padding: '10px', 89 background: updateCount > mutationCount ? '#c8e6c9' : '#ffcdd2', 90 borderRadius: '5px' 91 }}> 92 <p>✅ Successful updates: {updateCount}</p> 93 <p>❌ Failed mutations: {mutationCount}</p> 94 </div> 95 </div> 96 97 <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}> 98 <div> 99 <h4>❌ Wrong Way (Mutation)</h4> 100 <button 101 onClick={wrongWayToUpdateAge} 102 style={{ 103 padding: '10px', 104 background: '#ff5252', 105 color: 'white', 106 border: 'none', 107 borderRadius: '5px', 108 cursor: 'pointer', 109 width: '100%' 110 }} 111 > 112 Mutate Age (Won't Update UI) 113 </button> 114 <pre style={{ fontSize: '12px', marginTop: '10px' }}> 115{`person.age = person.age + 1; 116setPerson(person); 117// Same reference!`} 118 </pre> 119 </div> 120 121 <div> 122 <h4>✅ Right Way (New Object)</h4> 123 <button 124 onClick={correctWayToUpdateAge} 125 style={{ 126 padding: '10px', 127 background: '#4caf50', 128 color: 'white', 129 border: 'none', 130 borderRadius: '5px', 131 cursor: 'pointer', 132 width: '100%' 133 }} 134 > 135 Update Age (Will Update UI) 136 </button> 137 <pre style={{ fontSize: '12px', marginTop: '10px' }}> 138{`setPerson({ 139 ...person, 140 age: person.age + 1 141}); 142// New reference!`} 143 </pre> 144 </div> 145 </div> 146 147 <div style={{ 148 marginTop: '20px', 149 padding: '15px', 150 background: '#fff3e0', 151 borderRadius: '8px' 152 }}> 153 <h4>💡 Key Insight:</h4> 154 <p>After clicking "Mutate Age" several times, click "Update Age" once.</p> 155 <p>You'll see the age jump by all the mutations at once!</p> 156 <p>The mutations DID happen, React just didn't know about them.</p> 157 </div> 158 </div> 159 ); 160} 161 162export default MutationTrapDemo;

    React vergleicht den alten State mit dem neuen State mit Object.is(). Wenn du das gleiche Objekt mutierst und zurückgibst, sieht React keinen Unterschied - die Referenz ist ja dieselbe!

    Stell dir vor, du gibst jemandem ein Foto und sagst "Sag mir, wenn sich was ändert". Dann malst du heimlich was drauf, gibst ihm das gleiche Foto zurück und fragst "Hat sich was geändert?". Er sagt "Nein, ist immer noch das gleiche Foto!" (gleiche Referenz).

    Mit einem neuen Objekt ({...person}) gibst du ihm ein komplett neues Foto - da merkt er sofort den Unterschied!

    Das große Finale: useState + useEffect + useRef = ❤️

    Zeit, alles zusammenzubringen! Eine Todo-App, die zeigt, wie die drei Hooks harmonieren:

    1import { useState, useEffect, useRef } from 'react'; 2 3interface Todo { 4 id: number; 5 text: string; 6 completed: boolean; 7 createdAt: string; 8} 9 10type FilterType = 'all' | 'active' | 'completed'; 11 12function TodoMasterpiece() { 13 const [todos, setTodos] = useState<Todo[]>(() => { 14 // Lazy initial state from localStorage 15 const saved = localStorage.getItem('todos'); 16 return saved ? JSON.parse(saved) : []; 17 }); 18 19 const [inputValue, setInputValue] = useState<string>(''); 20 const [filter, setFilter] = useState<FilterType>('all'); 21 const [editingId, setEditingId] = useState<number | null>(null); 22 23 const inputRef = useRef<HTMLInputElement>(null); 24 const editInputRef = useRef<HTMLInputElement>(null); 25 26 // Save todos to localStorage 27 useEffect(() => { 28 localStorage.setItem('todos', JSON.stringify(todos)); 29 }, [todos]); 30 31 // Focus on edit input 32 useEffect(() => { 33 if (editingId && editInputRef.current) { 34 editInputRef.current.focus(); 35 editInputRef.current.select(); 36 } 37 }, [editingId]); 38 39 const addTodo = (e: React.FormEvent) => { 40 e.preventDefault(); 41 if (!inputValue.trim()) return; 42 43 setTodos(prev => [...prev, { 44 id: Date.now(), 45 text: inputValue, 46 completed: false, 47 createdAt: new Date().toISOString() 48 }]); 49 50 setInputValue(''); 51 inputRef.current?.focus(); // useRef for focus! 52 }; 53 54 const toggleTodo = (id: number) => { 55 setTodos(prev => prev.map(todo => 56 todo.id === id ? { ...todo, completed: !todo.completed } : todo 57 )); 58 }; 59 60 const deleteTodo = (id: number) => { 61 setTodos(prev => prev.filter(todo => todo.id !== id)); 62 }; 63 64 const updateTodo = (id: number, newText: string) => { 65 if (!newText.trim()) { 66 deleteTodo(id); 67 return; 68 } 69 70 setTodos(prev => prev.map(todo => 71 todo.id === id ? { ...todo, text: newText } : todo 72 )); 73 setEditingId(null); 74 }; 75 76 const filteredTodos = todos.filter(todo => { 77 if (filter === 'active') return !todo.completed; 78 if (filter === 'completed') return todo.completed; 79 return true; 80 }); 81 82 const stats = { 83 total: todos.length, 84 active: todos.filter(t => !t.completed).length, 85 completed: todos.filter(t => t.completed).length 86 }; 87 88 return ( 89 <div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}> 90 <h1 style={{ textAlign: 'center' }}>Todo Masterpiece 🎯</h1> 91 92 {/* Stats */} 93 <div style={{ 94 display: 'flex', 95 justifyContent: 'space-around', 96 padding: '20px', 97 background: '#f0f0f0', 98 borderRadius: '10px', 99 marginBottom: '20px' 100 }}> 101 <div style={{ textAlign: 'center' }}> 102 <div style={{ fontSize: '24px', fontWeight: 'bold' }}>{stats.total}</div> 103 <div style={{ fontSize: '12px', color: '#666' }}>Total</div> 104 </div> 105 <div style={{ textAlign: 'center' }}> 106 <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#2196F3' }}> 107 {stats.active} 108 </div> 109 <div style={{ fontSize: '12px', color: '#666' }}>Active</div> 110 </div> 111 <div style={{ textAlign: 'center' }}> 112 <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#4CAF50' }}> 113 {stats.completed} 114 </div> 115 <div style={{ fontSize: '12px', color: '#666' }}>Completed</div> 116 </div> 117 </div> 118 119 {/* Add Todo Form */} 120 <form onSubmit={addTodo} style={{ marginBottom: '20px' }}> 121 <input 122 ref={inputRef} 123 type="text" 124 value={inputValue} 125 onChange={(e) => setInputValue(e.target.value)} 126 placeholder="What needs to be done?" 127 style={{ 128 width: '100%', 129 padding: '12px', 130 fontSize: '16px', 131 border: '2px solid #ddd', 132 borderRadius: '5px' 133 }} 134 /> 135 </form> 136 137 {/* Filter */} 138 <div style={{ 139 display: 'flex', 140 justifyContent: 'center', 141 gap: '10px', 142 marginBottom: '20px' 143 }}> 144 {(['all', 'active', 'completed'] as const).map(f => ( 145 <button 146 key={f} 147 onClick={() => setFilter(f)} 148 style={{ 149 padding: '8px 16px', 150 background: filter === f ? '#2196F3' : '#fff', 151 color: filter === f ? '#fff' : '#333', 152 border: '1px solid #ddd', 153 borderRadius: '5px', 154 cursor: 'pointer' 155 }} 156 > 157 {f.charAt(0).toUpperCase() + f.slice(1)} 158 </button> 159 ))} 160 </div> 161 162 {/* Todo List */} 163 <div> 164 {filteredTodos.length === 0 ? ( 165 <p style={{ textAlign: 'center', color: '#999' }}> 166 {filter === 'all' ? 'No todos! Time to relax 🏖️' : 167 filter === 'active' ? 'No active todos! 🎉' : 168 'Nothing completed yet! 💪'} 169 </p> 170 ) : ( 171 filteredTodos.map(todo => ( 172 <div 173 key={todo.id} 174 style={{ 175 display: 'flex', 176 alignItems: 'center', 177 padding: '12px', 178 marginBottom: '8px', 179 background: todo.completed ? '#f0f0f0' : '#fff', 180 border: '1px solid #ddd', 181 borderRadius: '5px', 182 transition: 'all 0.3s' 183 }} 184 > 185 <input 186 type="checkbox" 187 checked={todo.completed} 188 onChange={() => toggleTodo(todo.id)} 189 style={{ marginRight: '12px' }} 190 /> 191 192 {editingId === todo.id ? ( 193 <input 194 ref={editInputRef} 195 type="text" 196 defaultValue={todo.text} 197 onBlur={(e) => updateTodo(todo.id, e.target.value)} 198 onKeyDown={(e) => { 199 if (e.key === 'Enter') { 200 updateTodo(todo.id, (e.target as HTMLInputElement).value); 201 } 202 if (e.key === 'Escape') { 203 setEditingId(null); 204 } 205 }} 206 style={{ 207 flex: 1, 208 padding: '4px', 209 fontSize: '16px', 210 border: '1px solid #2196F3' 211 }} 212 /> 213 ) : ( 214 <span 215 onClick={() => setEditingId(todo.id)} 216 style={{ 217 flex: 1, 218 textDecoration: todo.completed ? 'line-through' : 'none', 219 color: todo.completed ? '#999' : '#333', 220 cursor: 'text' 221 }} 222 > 223 {todo.text} 224 </span> 225 )} 226 227 <button 228 onClick={() => deleteTodo(todo.id)} 229 style={{ 230 marginLeft: '12px', 231 padding: '4px 8px', 232 background: '#ff4444', 233 color: '#fff', 234 border: 'none', 235 borderRadius: '3px', 236 cursor: 'pointer' 237 }} 238 > 239240 </button> 241 </div> 242 )) 243 )} 244 </div> 245 246 {todos.length > 0 && ( 247 <button 248 onClick={() => setTodos([])} 249 style={{ 250 width: '100%', 251 marginTop: '20px', 252 padding: '12px', 253 background: '#ff4444', 254 color: '#fff', 255 border: 'none', 256 borderRadius: '5px', 257 cursor: 'pointer' 258 }} 259 > 260 🗑️ Clear all 261 </button> 262 )} 263 </div> 264 ); 265} 266 267export default TodoMasterpiece;

    Was macht die TodoMasterpiece so besonders?

    Diese Todo-App ist das perfekte Finale unserer Basics-Trilogie, weil hier alle drei Hooks in perfekter Harmonie zusammenarbeiten:

    useState macht den Hauptjob:

    • todos - Die Liste selbst (mit Lazy Initial State aus localStorage!)
    • inputValue - Der Text im Eingabefeld
    • filter - Welche Todos werden angezeigt
    • editingId - Welches Todo wird gerade bearbeitet

    useEffect kümmert sich um Seiteneffekte:

    • Speichert Todos automatisch in localStorage bei jeder Änderung
    • Fokussiert das Edit-Feld, wenn du ein Todo bearbeitest
    • Keine manuellen Saves nötig - es passiert einfach!

    useRef hält die DOM-Referenzen:

    • inputRef - Fokussiert das Eingabefeld nach dem Hinzufügen
    • editInputRef - Fokussiert und selektiert beim Bearbeiten
    • Kein Re-Render nötig für diese DOM-Operationen!

    Die Magie liegt im Zusammenspiel: Wenn du ein Todo hinzufügst, updated useState die Liste, useEffect speichert sie automatisch, und useRef sorgt dafür, dass der Fokus wieder im Eingabefeld landet. Alles greift ineinander wie Zahnräder in einem Uhrwerk.

    Das ist React, wie es sein sollte: Jeder Hook hat seinen Job, keiner macht zu viel, und zusammen erschaffen sie eine flüssige User Experience. Nach dieser Trilogie versteht ihr nicht nur, WAS die Hooks machen, sondern auch WARUM sie so gut zusammenarbeiten.

    Die useState Weisheiten

    Nach all dem, hier meine gesammelten Erkenntnisse:

    Die Goldenen Regeln:

    1. State ist asynchron - Nach setState ist der neue Wert noch nicht da
    2. Funktionale Updates - Nutze prev => ... für Updates die vom alten Wert abhängen
    3. Immutability ist König - Niemals State direkt mutieren!
    4. Batching ist dein Freund - React optimiert für dich
    5. Lazy Initial State - Für teure Berechnungen
    6. Weniger ist mehr - Nicht alles muss State sein

    Wann useState, wann nicht?

    useState ist richtig für:

    • UI-State (offen/geschlossen, aktiv/inaktiv)
    • Formulardaten
    • Dynamische Listen
    • Alles was Re-Render triggern soll

    KEIN useState für:

    • Werte die aus anderen States berechnet werden können
    • Konstanten
    • Refs zu DOM-Elementen (→ useRef)
    • Timer-IDs (→ useRef)

    Zum Abschluss

    So, jetzt habt ihr die "Basics" - nachdem ihr schon die fortgeschrittenen Sachen kennt. Macht das Sinn? Nein. War's trotzdem gut? Hoffentlich!

    useState mag simpel aussehen, aber wie ihr gesehen habt, steckt mehr dahinter als const [x, setX] = useState(0). Die Kombination mit useEffect und useRef macht React erst richtig mächtig.

    Was kommt als nächstes? Vielleicht useContext für globalen State? useReducer für die Redux-Nostalgiker? useMemo für die Performance-Freaks? useCallback für... okay, ich hör schon auf.

    Eines ist sicher: Nach dieser Rückwärts-Trilogie seid ihr bereit für alles, was React euch entgegenwirft!

    Die Basics-Trilogie - Komplett!

    Glückwunsch! Du hast die komplette Basics-Trilogie durchgearbeitet - nur halt rückwärts. 🎉

    Die komplette Trilogie:

    1. Der useRef Hook - Das Schweizer Taschenmesser
    2. Der useEffect Hook - Der Hook für Seiteneffekte
    3. Der useState Hook - Du bist hier!

    Falls du die anderen Teile verpasst hast, hol das nach! Die Reihenfolge ist zwar unkonventionell, aber am Ende fügt sich alles zusammen wie ein gut geschüttelter Cocktail. 🍹

    Alle Beispiele für CodeSandbox

    1. NotSoEasyCounter - State Updates verstehen
    2. LazyInitialStateDemo - Lazy Initial State
    3. MutationTrapDemo - Objekte und Arrays - Die Referenz-Falle
    4. TodoMasterpiece - Alle drei Hooks vereint

    Weiterführende Links

    Comments: