Formulare in React
Meistere Formulare in React mit kontrollierten Komponenten. Validierung, verschiedene Eingabetypen und Best Practices.
Formulare in React
Formulare sind ein zentraler Bestandteil fast jeder Webanwendung – Login, Registrierung, Suche, Kontaktformulare. In React funktionieren sie etwas anders als in normalem HTML. Hier lernst du alles, was du wissen musst.
Kontrollierte Komponenten
In React ist der State die einzige Quelle der Wahrheit fuer Formularfelder. Das nennt man kontrollierte Komponenten:
import { useState } from 'react';
function SimpleForm() {
const [name, setName] = useState('');
return (
<input
type="text"
value={name} // State bestimmt den Wert
onChange={(e) => setName(e.target.value)} // Aenderung aktualisiert State
/>
);
}
Wie es funktioniert
1. User tippt "M"
2. onChange wird ausgeloest
3. setName('M') aktualisiert den State
4. Komponente rendert neu
5. Input zeigt 'M' an (aus dem State)
Verschiedene Eingabetypen
Text-Input
function TextInputs() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<form>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="E-Mail"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Passwort"
/>
</form>
);
}
Textarea
function TextArea() {
const [message, setMessage] = useState('');
return (
<div>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
rows={5}
placeholder="Deine Nachricht..."
/>
<p>{message.length}/500 Zeichen</p>
</div>
);
}
Checkbox
function CheckboxExample() {
const [accepted, setAccepted] = useState(false);
return (
<label>
<input
type="checkbox"
checked={accepted}
onChange={(e) => setAccepted(e.target.checked)}
/>
Ich akzeptiere die AGB
</label>
);
}
Radio Buttons
function RadioExample() {
const [size, setSize] = useState('medium');
return (
<div>
<p>Groesse waehlen:</p>
{['small', 'medium', 'large'].map(option => (
<label key={option} style={{ marginRight: '1rem' }}>
<input
type="radio"
name="size"
value={option}
checked={size === option}
onChange={(e) => setSize(e.target.value)}
/>
{option}
</label>
))}
<p>Gewaehlt: {size}</p>
</div>
);
}
Select (Dropdown)
function SelectExample() {
const [country, setCountry] = useState('');
return (
<div>
<select value={country} onChange={(e) => setCountry(e.target.value)}>
<option value="">Land waehlen...</option>
<option value="de">Deutschland</option>
<option value="at">Oesterreich</option>
<option value="ch">Schweiz</option>
</select>
{country && <p>Gewaehlt: {country}</p>}
</div>
);
}
Formulare mit mehreren Feldern
Statt fuer jedes Feld einen eigenen State zu verwenden, nutze ein Objekt:
function RegistrationForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: '',
confirmPassword: '',
newsletter: false
});
function handleChange(e) {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
}
function handleSubmit(e) {
e.preventDefault();
console.log('Formulardaten:', formData);
}
return (
<form onSubmit={handleSubmit}>
<input
name="username"
value={formData.username}
onChange={handleChange}
placeholder="Benutzername"
/>
<input
name="email"
type="email"
value={formData.email}
onChange={handleChange}
placeholder="E-Mail"
/>
<input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="Passwort"
/>
<input
name="confirmPassword"
type="password"
value={formData.confirmPassword}
onChange={handleChange}
placeholder="Passwort bestaetigen"
/>
<label>
<input
name="newsletter"
type="checkbox"
checked={formData.newsletter}
onChange={handleChange}
/>
Newsletter abonnieren
</label>
<button type="submit">Registrieren</button>
</form>
);
}
Formular-Validierung
Einfache Validierung
function ValidatedForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState({});
function validate() {
const newErrors = {};
if (!email) {
newErrors.email = 'E-Mail ist erforderlich';
} else if (!email.includes('@')) {
newErrors.email = 'Ungueltige E-Mail-Adresse';
}
if (!password) {
newErrors.password = 'Passwort ist erforderlich';
} else if (password.length < 8) {
newErrors.password = 'Passwort muss mindestens 8 Zeichen lang sein';
}
return newErrors;
}
function handleSubmit(e) {
e.preventDefault();
const newErrors = validate();
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
setErrors({});
console.log('Formular ist gueltig!', { email, password });
}
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="E-Mail"
style={{ borderColor: errors.email ? 'red' : '#ccc' }}
/>
{errors.email && (
<p style={{ color: 'red', fontSize: '0.85rem' }}>{errors.email}</p>
)}
</div>
<div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Passwort"
style={{ borderColor: errors.password ? 'red' : '#ccc' }}
/>
{errors.password && (
<p style={{ color: 'red', fontSize: '0.85rem' }}>{errors.password}</p>
)}
</div>
<button type="submit">Anmelden</button>
</form>
);
}
Live-Validierung
function LiveValidation() {
const [password, setPassword] = useState('');
const checks = [
{ label: 'Mindestens 8 Zeichen', valid: password.length >= 8 },
{ label: 'Mindestens ein Grossbuchstabe', valid: /[A-Z]/.test(password) },
{ label: 'Mindestens eine Zahl', valid: /[0-9]/.test(password) },
{ label: 'Mindestens ein Sonderzeichen', valid: /[^A-Za-z0-9]/.test(password) }
];
const allValid = checks.every(c => c.valid);
return (
<div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Sicheres Passwort"
/>
<ul style={{ listStyle: 'none', padding: 0 }}>
{checks.map((check, i) => (
<li key={i} style={{ color: check.valid ? 'green' : 'red' }}>
{check.valid ? '✓' : '✗'} {check.label}
</li>
))}
</ul>
<button disabled={!allValid}>Weiter</button>
</div>
);
}
Praktisches Beispiel: Kontaktformular
function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: ''
});
const [submitted, setSubmitted] = useState(false);
const [errors, setErrors] = useState({});
function handleChange(e) {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
// Fehler beim Tippen entfernen
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }));
}
}
function handleSubmit(e) {
e.preventDefault();
const newErrors = {};
if (!formData.name.trim()) newErrors.name = 'Name erforderlich';
if (!formData.email.includes('@')) newErrors.email = 'Gueltige E-Mail erforderlich';
if (!formData.subject) newErrors.subject = 'Betreff waehlen';
if (formData.message.length < 10) newErrors.message = 'Mindestens 10 Zeichen';
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
console.log('Gesendet:', formData);
setSubmitted(true);
}
if (submitted) {
return (
<div style={{ textAlign: 'center', padding: '2rem' }}>
<h2 style={{ color: '#27ae60' }}>Nachricht gesendet!</h2>
<p>Vielen Dank, {formData.name}. Wir melden uns bald.</p>
<button onClick={() => {
setSubmitted(false);
setFormData({ name: '', email: '', subject: '', message: '' });
}}>
Neue Nachricht
</button>
</div>
);
}
return (
<form onSubmit={handleSubmit} style={{ maxWidth: '500px' }}>
<h2>Kontaktformular</h2>
<div style={{ marginBottom: '1rem' }}>
<label>Name</label>
<input
name="name"
value={formData.name}
onChange={handleChange}
style={{ width: '100%', padding: '8px' }}
/>
{errors.name && <small style={{ color: 'red' }}>{errors.name}</small>}
</div>
<div style={{ marginBottom: '1rem' }}>
<label>E-Mail</label>
<input
name="email"
type="email"
value={formData.email}
onChange={handleChange}
style={{ width: '100%', padding: '8px' }}
/>
{errors.email && <small style={{ color: 'red' }}>{errors.email}</small>}
</div>
<div style={{ marginBottom: '1rem' }}>
<label>Betreff</label>
<select
name="subject"
value={formData.subject}
onChange={handleChange}
style={{ width: '100%', padding: '8px' }}
>
<option value="">Bitte waehlen...</option>
<option value="support">Support</option>
<option value="feedback">Feedback</option>
<option value="other">Sonstiges</option>
</select>
{errors.subject && <small style={{ color: 'red' }}>{errors.subject}</small>}
</div>
<div style={{ marginBottom: '1rem' }}>
<label>Nachricht</label>
<textarea
name="message"
value={formData.message}
onChange={handleChange}
rows={5}
style={{ width: '100%', padding: '8px' }}
/>
{errors.message && <small style={{ color: 'red' }}>{errors.message}</small>}
</div>
<button type="submit" style={{
padding: '10px 20px',
backgroundColor: '#3498db',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer'
}}>
Absenden
</button>
</form>
);
}
Uebungen
- Erstelle ein Login-Formular mit E-Mail, Passwort und Validierung
- Baue einen Mehrstufigen Wizard mit 3 Schritten (persoenliche Daten, Adresse, Zusammenfassung)
- Erstelle ein Such-Formular, das eine Liste live filtert waehrend der User tippt
Was kommt als Naechstes?
Du beherrschst jetzt Formulare in React. Im naechsten Kapitel lernst du bedingtes Rendern – wie du verschiedene UI-Zustaende elegant darstellst, je nachdem was in deiner App gerade passiert.
Zusammenfassung
- Kontrollierte Komponenten nutzen State als einzige Quelle der Wahrheit
- Jedes Formularfeld braucht einen
valueund einenonChange-Handler - Nutze ein Formular-Objekt statt vieler einzelner States
- Validierung kann beim Submit oder live beim Tippen erfolgen
- Verwende immer
e.preventDefault()im Submit-Handler - Zeige Fehlermeldungen direkt beim betroffenen Feld an
Pro-Tipp: Fuer komplexe Formulare mit vielen Feldern und Validierungsregeln lohnt sich eine Bibliothek wie React Hook Form oder Formik. Sie reduzieren Boilerplate-Code und bieten maechtige Validierungs-Features out of the box!