React State Management mit React Bootstrap
In diesem Abschnitt lernen wir die Grundlagen des State Managements in React mit dem useState-Hook kennen und integrieren React Bootstrap für eine ansprechendere Benutzeroberfläche.
Was ist React State?
Abschnitt betitelt „Was ist React State?“State ist ein Objekt, das Daten enthält, die sich während des Lebenszyklus einer Komponente ändern können. Wenn sich der State ändert, wird die Komponente automatisch neu gerendert, um die Änderungen anzuzeigen.
1. useState Hook verstehen
Abschnitt betitelt „1. useState Hook verstehen“Der useState-Hook ist eine Funktion, die ein State-Variable und eine Funktion zum Aktualisieren dieser Variable zurückgibt:
const [state, setState] = useState(initialValue);state: Der aktuelle Wert des StatessetState: Eine Funktion zum Aktualisieren des StatesinitialValue: Der Anfangswert des States
2. SearchBar-Komponente mit State und React Bootstrap
Abschnitt betitelt „2. SearchBar-Komponente mit State und React Bootstrap“Wir verwenden die bereits erstellte SearchBar-Komponente und erweitern sie mit State-Management:
import { useState } from "react";import { Form, Button, InputGroup } from "react-bootstrap";import { FaSearch } from "react-icons/fa";
function SearchBar({ onSearch }) { const [query, setQuery] = useState("");
const handleSubmit = (e) => { e.preventDefault(); if (query.trim()) { onSearch(query); } };
return ( <div className="mb-4"> <Form onSubmit={handleSubmit}> <InputGroup> <Form.Control type="text" placeholder="Suche nach Buchtitel, Autor, ISBN..." value={query} onChange={(e) => setQuery(e.target.value)} aria-label="Suchbegriff" /> <Button variant="primary" type="submit"> <FaSearch className="me-1" /> Suchen </Button> </InputGroup> </Form> </div> );}
export default SearchBar;3. State für Bücherliste in App.jsx
Abschnitt betitelt „3. State für Bücherliste in App.jsx“Aktualisieren wir unsere App.jsx, um die Bücherliste als State zu verwalten und die Suche mit React Bootstrap-Komponenten zu implementieren:
import { useState } from "react";import { Container, Alert, Spinner } from "react-bootstrap";import Header from "./components/Header/Header";import SearchBar from "./components/SearchBar/SearchBar";import BookList from "./components/BookList/BookList";
function App() { // Beispiel-Bücher const initialBooks = [ { id: "1", title: "Die unendliche Geschichte", author: "Michael Ende", imageUrl: "https://placehold.co/128x192", description: "Ein Junge namens Bastian gerät in eine fantastische Welt, als er ein geheimnisvolles Buch liest.", }, { id: "2", title: "Harry Potter und der Stein der Weisen", author: "J.K. Rowling", imageUrl: "https://placehold.co/128x192", description: "Ein Waisenjunge erfährt, dass er ein Zauberer ist und beginnt sein Abenteuer in Hogwarts.", }, { id: "3", title: "Der Herr der Ringe", author: "J.R.R. Tolkien", imageUrl: "https://placehold.co/128x192", description: "Die epische Geschichte von Frodo Beutlin und seiner Reise, um den Einen Ring zu zerstören.", }, ];
// State für Bücherliste const [books, setBooks] = useState(initialBooks); // State für den Ladevorgang const [isLoading, setIsLoading] = useState(false); // State für Fehlermeldungen const [error, setError] = useState(null); // State für die aktuelle Suche const [searchQuery, setSearchQuery] = useState("");
const handleSearch = (query) => { setSearchQuery(query); setIsLoading(true); setError(null);
// Simuliere eine API-Anfrage mit setTimeout setTimeout(() => { try { // Filter die Bücher basierend auf der Suchanfrage const filteredBooks = initialBooks.filter( (book) => book.title.toLowerCase().includes(query.toLowerCase()) || book.author.toLowerCase().includes(query.toLowerCase()) );
setBooks(filteredBooks); setIsLoading(false); } catch (err) { setError("Fehler bei der Suche. Bitte versuche es erneut."); setIsLoading(false); } }, 1000); // 1 Sekunde Verzögerung, um Ladevorgang zu simulieren };
return ( <div className="App"> <Header /> <Container className="mt-4"> <h2>Willkommen im Bücher-Projekt</h2> <p>Entdecke neue Bücher und verwalte deine Favoriten.</p>
<SearchBar onSearch={handleSearch} />
{searchQuery && ( <Alert variant="info"> Ergebnisse für: <strong>{searchQuery}</strong> </Alert> )}
{isLoading ? ( <div className="text-center my-5"> <Spinner animation="border" role="status" variant="primary" /> <p className="mt-2">Bücher werden geladen...</p> </div> ) : error ? ( <Alert variant="danger">{error}</Alert> ) : ( <> <h3>Gefundene Bücher</h3> <BookList books={books} /> </> )} </Container> </div> );}
export default App;4. BookCard-Komponente mit React Bootstrap und Favoriten-Funktion
Abschnitt betitelt „4. BookCard-Komponente mit React Bootstrap und Favoriten-Funktion“Aktualisieren wir unsere BookCard-Komponente, um React Bootstrap zu verwenden und das Speichern von Favoriten zu ermöglichen:
import { useState } from "react";import { Card, Button } from "react-bootstrap";import { FaHeart, FaRegHeart, FaInfoCircle } from "react-icons/fa";
function BookCard({ title, author, imageUrl, description }) { const [isFavorite, setIsFavorite] = useState(false);
const toggleFavorite = () => { setIsFavorite(!isFavorite); };
return ( <Card className="h-100 shadow-sm"> <div className="d-flex h-100"> <div style={{ width: "128px", minWidth: "128px" }}> <Card.Img src={imageUrl || "https://placehold.co/128x192"} alt={`Cover of ${title}`} style={{ height: "100%", objectFit: "cover" }} /> </div> <Card.Body> <Card.Title>{title}</Card.Title> <Card.Subtitle className="mb-2 text-muted"> von {author || "Unbekannter Autor"} </Card.Subtitle> <Card.Text> {description ? description.length > 150 ? description.substring(0, 147) + "..." : description : "Keine Beschreibung verfügbar."} </Card.Text> <div className="d-flex gap-2 mt-auto"> <Button variant="primary" size="sm"> <FaInfoCircle className="me-1" /> Details </Button> <Button variant={isFavorite ? "danger" : "outline-danger"} size="sm" onClick={toggleFavorite} aria-label={ isFavorite ? "Aus Favoriten entfernen" : "Zu Favoriten hinzufügen" } > {isFavorite ? <FaHeart /> : <FaRegHeart />} </Button> </div> </Card.Body> </div> </Card> );}
export default BookCard;5. BookList-Komponente mit React Bootstrap
Abschnitt betitelt „5. BookList-Komponente mit React Bootstrap“Aktualisieren wir die BookList-Komponente, um React Bootstrap zu nutzen:
import { Row, Col, Alert } from "react-bootstrap";import BookCard from "../BookCard/BookCard";
function BookList({ books }) { if (!books || books.length === 0) { return <Alert variant="secondary">Keine Bücher gefunden.</Alert>; }
return ( <Row className="g-4"> {books.map((book) => ( <Col key={book.id} xs={12} md={6} lg={6}> <BookCard title={book.title} author={book.author} imageUrl={book.imageUrl} description={book.description} /> </Col> ))} </Row> );}
export default BookList;6. AddBookForm mit React Bootstrap und State
Abschnitt betitelt „6. AddBookForm mit React Bootstrap und State“Erstellen wir ein Formular zum Hinzufügen eines neuen Buches mit React Bootstrap:
import { useState } from "react";import { Form, Button, Card, Row, Col } from "react-bootstrap";import { FaPlus, FaTimes } from "react-icons/fa";
function AddBookForm({ onAddBook }) { const [formData, setFormData] = useState({ title: "", author: "", description: "", imageUrl: "", });
const [isFormOpen, setIsFormOpen] = useState(false);
const handleChange = (e) => { const { name, value } = e.target; setFormData((prevData) => ({ ...prevData, [name]: value, })); };
const handleSubmit = (e) => { e.preventDefault(); onAddBook({ ...formData, id: Date.now().toString(), // Einfache ID-Generierung für das Beispiel }); // Formular zurücksetzen setFormData({ title: "", author: "", description: "", imageUrl: "", }); // Formular schliessen setIsFormOpen(false); };
return ( <div className="my-4"> {!isFormOpen ? ( <Button variant="success" className="w-100 py-3" onClick={() => setIsFormOpen(true)} > <FaPlus className="me-2" /> Neues Buch hinzufügen </Button> ) : ( <Card className="border-success"> <Card.Header className="bg-success text-white"> <h5 className="mb-0">Neues Buch hinzufügen</h5> </Card.Header> <Card.Body> <Form onSubmit={handleSubmit}> <Row> <Col md={6}> <Form.Group className="mb-3" controlId="bookTitle"> <Form.Label>Titel</Form.Label> <Form.Control type="text" name="title" value={formData.title} onChange={handleChange} required /> </Form.Group> </Col> <Col md={6}> <Form.Group className="mb-3" controlId="bookAuthor"> <Form.Label>Autor</Form.Label> <Form.Control type="text" name="author" value={formData.author} onChange={handleChange} required /> </Form.Group> </Col> </Row>
<Form.Group className="mb-3" controlId="bookDescription"> <Form.Label>Beschreibung</Form.Label> <Form.Control as="textarea" name="description" value={formData.description} onChange={handleChange} rows={4} /> </Form.Group>
<Form.Group className="mb-3" controlId="bookImageUrl"> <Form.Label>Bild-URL</Form.Label> <Form.Control type="text" name="imageUrl" value={formData.imageUrl} onChange={handleChange} placeholder="https://example.com/book-cover.jpg" /> </Form.Group>
<div className="d-flex gap-2"> <Button variant="primary" type="submit"> Buch hinzufügen </Button> <Button variant="secondary" onClick={() => setIsFormOpen(false)} > <FaTimes className="me-1" /> Abbrechen </Button> </div> </Form> </Card.Body> </Card> )} </div> );}
export default AddBookForm;7. AddBookForm in App.jsx integrieren
Abschnitt betitelt „7. AddBookForm in App.jsx integrieren“Aktualisiere die App.jsx, um das Formular zu integrieren:
// In App.jsx importierenimport AddBookForm from "./components/AddBookForm/AddBookForm";
// Innerhalb der App-Komponente die Funktion hinzufügen:const handleAddBook = (newBook) => { setBooks([...books, newBook]);};
// Im JSX Teil vor dem BookList einfügen:<AddBookForm onAddBook={handleAddBook} />;Der aktualisierte Abschnitt in App.jsx sieht so aus:
return ( <div className="App"> <Header /> <Container className="mt-4"> <h2>Willkommen im Bücher-Projekt</h2> <p>Entdecke neue Bücher und verwalte deine Favoriten.</p>
<SearchBar onSearch={handleSearch} />
{searchQuery && ( <Alert variant="info"> Ergebnisse für: <strong>{searchQuery}</strong> </Alert> )}
<AddBookForm onAddBook={handleAddBook} />
{isLoading ? ( <div className="text-center my-5"> <Spinner animation="border" role="status" variant="primary" /> <p className="mt-2">Bücher werden geladen...</p> </div> ) : error ? ( <Alert variant="danger">{error}</Alert> ) : ( <> <h3>Gefundene Bücher</h3> <BookList books={books} /> </> )} </Container> </div>);Zusammenfassung
Abschnitt betitelt „Zusammenfassung“In diesem Abschnitt haben wir gelernt:
- Den
useState-Hook in React zu nutzen - Eine Suchleiste mit State und React Bootstrap zu implementieren
- Den State für eine Bücherliste zu verwalten
- Die Favoriten-Funktion zu implementieren
- Ein Formular mit mehreren State-Variablen zu erstellen
- Neue Bücher zur Liste hinzuzufügen
- React Bootstrap-Komponenten für ein modernes UI einzusetzen, ohne zusätzliches CSS zu schreiben
Im nächsten Abschnitt werden wir uns mit dem useEffect-Hook beschäftigen und lernen, wie wir APIs einbinden können, um echte Buchdaten abzurufen.