Komponenten können einen internen Zustand (State) haben
Auf den State kann im Template verwiesen werden
Die Anzeige wird automatisch aktualisiert, wenn Teile des States neu gesetzt werden
State Hook in Funktionskomponenten:
import { useState } from 'react';
function Slideshow() {
const [imgId, setImgId] = useState(0);
const [imgWidth, setImgWidth] = useState(300);
const [imgHeight, setImgHeight] = useState(200);
return ...
};
const [imgId, setImgId] = useState(0);
useState
kann in der Komponentenfunktion (wiederholt) aufgerufen werden, um State-Einträge zu deklarieren
Parameter: ursprünglicher State
Array von Rückgabewerten: aktueller State, Setter-Funktion
Richlinie: Verwende den minimalen State (d.h. keine redundanten Daten)
Weitere Daten können aus dem State abgeleitet werden
Beispiele:
Implementiere die zuvor gesehene Slideshow-Komponente erneut; versuche, nicht auf den bisherigen Code zu blicken
https://picsum.photos/300/200?image=0
Füge weitere Features hinzu, z.B.:
vermeide vorerst folgendes:
setTimeout
, setInterval
)ein "uncontrolled input" in React:
<input />
React kennt den Inhalt des Inputs nicht und kann ihn nicht ändern
So können wir den Wert eines Inputs im State erfassen:
const [inputText, setInputText] = useState('');
<input
value={inputText}
onChange={(event) => setInputText(event.target.value)}
/>
Erklärung:
value={inputText}
bindet vom State auf den Wert des Inputs
onChange={(event) => setInputText(event.target.value)}
aktualisiert den State, wenn sich der Wert des Inputs ändert
warum event.target.value
?
event.target
repräsentiert das Input-Elementevent.target.value
ist der neue Wert des Input-ElementsBeispiel: Input, bei dem auch die Textlänge angezeigt wird:
function App() {
const [text, setText] = useState('');
return (
<div>
<input
value={text}
onChange={(event) => setText(event.target.value)}
/>
<button onClick={() => setText('')}>clear</button>
<p>This string has {text.length} characters.</p>
</div>
);
}
andere Möglichkeiten zum Arbeiten mit Input-State:
FormData
)Text und Textarea:
<input
value={title}
onChange={(event) => setTitle(event.target.value)}
/>
Checkbox:
<input
type="checkbox"
checked={accept}
onChange={(event) => setAccept(event.target.checked)}
/>
Grundlegender Rat für numerische Inputs:
speichere den Inhalt als String (nicht als Zahl)
Grund: mögliche Inhalte eines Numerischen Inputs (während der Benutzer tippt):
""
"-"
"-3"
"-3."
"-3.0"
Beispiel für numerischen Input mit direkten "Auswirkungen":
https://codesandbox.io/s/numeric-input-direct-results-5vde88
Erstelle eine Komponente namens NewsletterSignup
mit drei State-Einträgen:
email
, repeatEmail
, acceptTerms
Demo einer vollständigen Version: https://codesandbox.io/s/newsletter-form-pvgs6l
Bemerkung: aktivieren / deaktivieren eines buttons in JSX: <button disabled={...}>...</button>
Extra: füge ein Dropdown hinzu, mit dem eine Sprache für den Newsletter ausgewählt werden kann
Immutability: Wichtiges Konzept in der funktionalen Programmierung und bei React
Daten werden nicht direkt abgeändert - stattdessen werden neue Daten auf Basis der alten generiert
Wenn unser State Arrays oder Objekte enthält, könnten wir versuchen, diese direkt abzuändern
Das sollten wir nicht tun - React bemerkt diese Änderungen nicht und aktualisiert die Ansicht eventuell nicht
Objekte im State sollten als unveränderlich erachtet werden
Wenn ein State-Setter aufgerufen wird:
React vergleicht die alte State-Referenz mit der neuen State-Referenz
Wenn die Referenz die gleiche ist, wird die Komponente nicht neu gerendert
Möglichkeiten, um aus bestehenden Daten aktualisierte Daten zu erhalten:
Themen:
Mechanismen:
...
).map
, .filter
)Spread Syntax für Arrays:
const numbers = [1, 2, 3];
const moreNumbers = [...numbers, 4, 5, 6];
// moreNumbers: [1, 2, 3, 4, 5, 6]
Spread Syntax für Objekte:
const person = {
firstName: 'Joe',
lastName: 'Doe',
age: 31,
};
const newPerson = { ...person, email: 'j@d.com', age: 32 };
// {firstName: 'Joe', lastName: 'Doe', email: 'j@d.com', age: 32}
die .map
-Methode:
const myNumbers = [1, 2, 3, 4];
const tripledNumbers = myNumbers.map((n) => 3 * n);
// [3, 6, 9, 12]
const squareRoots = myNumbers.map(Math.sqrt);
// [1, 1.41, 1.73, 2]
die .filter
-Methode:
const myNumbers = [1, 2, 3, 4];
const evenNumbers = myNumbers.filter((n) => n % 2 === 0);
// [2, 4]
Mechanismen:
Arbeiten mit Todos
Aufgabe: Vervollständige Funktionen, die Todos verwalten (ohne Mutationen)
Ãœbung: Erstelle die folgende reine Funktion, die ein Todo verarbeitet:
const todo1 = { id: 1, title: 'foo', completed: false };
function changeTitle(todo, newTitle) {
// TODO: FINISH IMPLEMENTATION HERE
}
const todo2 = changeTitle(todo1, 'bar');
console.log(todo2);
// { id: 1, title: 'bar', completed: false}
Lösung:
function changeTitle(todo, newTitle) {
return { ...todo, title: newTitle };
}
Ãœbungen: Erstelle reine Funktionen, die ein Array von Todo-Objekten behandeln:
const todos = [
{ id: 1, title: 'foo', completed: false },
{ id: 5, title: 'bar', completed: true },
{ id: 7, title: 'baz', completed: false },
];
Vervollständige diesen Code, sodass er ein neues, unerledigtes Todo mit einem gegebenen Titel und einer neuen ID hinzufügt:
function addTodo(todos, newTitle) {
let maxId = 0;
for (let todo of todos) {
maxId = Math.max(maxId, todo.id);
}
// TODO: FINISH IMPLEMENTATION HERE
}
// add a todo with title 'qux'
const todosA = addTodo(todos, 'qux');
console.log(todosA);
mögliche Lösung:
function addTodo(todos, newTitle) {
let maxId = 0;
for (let todo of todos) {
maxId = Math.max(maxId, todo.id);
}
return [
...todos,
{ id: maxId + 1, title: newTitle, completed: false },
];
}
Vervollständige diesen Code, sodass er ein bestimmtes Todo anhand der ID löscht:
function removeTodo(todos, id) {
// TODO: FINISH IMPLEMENTATION HERE
}
const todosB = removeTodo(todos, 1);
console.log(todosB);
mögliche Lösung:
function removeTodo(todos, id) {
return todos.filter((todo) => todo.id !== id);
}
Vervollständige diesen Code, sodass er den completed-Zustand eines bestimmten Todos ändert
function changeTodoCompleted(todos, id, completed) {
// TODO: FINISH IMPLEMENTATION HERE
}
// change the completed status of todo 1 to true
const todosC = changeTodoCompleted(todos, 1, true);
console.log(todosC);
mögliche Lösung:
function changeTodoCompleted(todos, id, completed) {
return todos.map((todo) => {
if todo.id === id {
return {...todo, completed: completed}
} else {
return todo;
}
})
}
Newsletter-Anmeldung von zuvor: Speichere den State in einem einzelnen Objekt mit drei Einträgen
const [data, setData] = useState({
email: '',
repeatEmail: '',
acceptTerms: false,
});
Todos: Schreibe weitere reine Funktionen, die mit Todos arbeiten:
changeTodoTitle
toggleTodo
(umschalten zwischen erledigt und nicht-erledigt)updateTodo
(ändern von title
und completed
in einem Aufruf)removeAllCompletedTodos