Der Umgang mit Formularen in React erfordert ein präzises Verständnis des Komponentenstatus. Dabei spielt der useState-Hook eine zentrale Rolle, um Eingabewerte zu erfassen, zu aktualisieren und den Zustand kontrolliert zu verwalten. Besonders bei komplexeren Formularen ist ein sauber strukturiertes State-Management entscheidend für eine nachvollziehbare und wartbare Anwendung.

Einfaches Formular

Im ersten Beispiel möchte ich ein einfaches Formular aufbauen und die Verwendung von useState() nochmals aufzeigen. Wir arbeiten mit useState(), um die Werte aus dem Formular zu überwachen und beim Absenden abrufen zu können.

TypeScript StateExample.jsx
import { useState } from 'react';

const SimpleForm = () => {
    const [email, setEmail] = useState('');
    const [formSubmitted, setFormSubmitted] = useState(false);

    const handleUpdateEmail = (event) => {
        setEmail(currentState => event.target.value);
    };

    const handleSubmit = (event) => {
        event.preventDefault();
        setFormSubmitted(currentState => true);
        console.log('Input value:', email);
    };

    return (
        <>
            <form onSubmit={handleSubmit}>
                <label htmlFor="email_field">E-Mail</label>
                <input
                    type="email"
                    id="email_field"
                    name="email_field"
                    value={email}
                    onChange={handleUpdateEmail}
                />

                <button type="submit">
                    Absenden
                </button>
            </form>

            {email.length > 0 && email !== '' && (
                <p>Aktuelle Eingabe: {email}</p>
            )}

            {formSubmitted && (
                <p>Das Formular wurde abgesendet.</p>
            )}
        </>
    );
};

export default SimpleForm;

Beschreibung des Components

In diesem Component werden zwei Zustandswerte verwendet.

JavaScript Überwachung
const [email, setEmail] = useState('');

Dieser Zustandswert dient der tatsächlichen Überwachung des Formular-Feldes für die E-Mail.

JavaScript Beispiel
const [formSubmitted, setFormSubmitted] = useState(false);

Dieser Zustandswert ist eher unterstützend hier eingesetzt, um das Absenden des Formular zu simulieren und ein Element einzublenden, welcher in der UI mitteilt, dass das Formular abgesendet wurde.

React State - Formular mit einem Feld - Beispiel

Außerdem haben wir hier eine Art Two-Way-Binding am Eingabefeld mit value={email} definiert. Im JSX-Template verwenden wir hierfür <p>Aktuelle Eingabe: {email}</p>, um bei jeder Eingabe einen aktualisierten Wert zu erhalten.

Im Absende-Moment setzen wir unseren unterstützenden Zustandswert auf true, um den Paragraphen mit der Mitteilung anzuzeigen. Ebenfalls haben wir Zugriff auf den aktuellen Wert des E-Mail Zustandswertes. Diesen können wir tatsächlich verwenden, um diese E-Mail über eine API an einen Server zu senden.

Kontrollierte Komponente

In diesem Beispiel ist das Eingabefeld eine kontrollierte Komponente. Das bedeutet, dass der Wert des Feldes vollständig durch den React State gesteuert wird. Jede Änderung wird durch den onChange Handler erfasst und aktualisiert den State.

Formular mit mehreren Feldern

Im zweiten Beispiel erhöhen wir die Anzahl der Felder. An dieser Stelle macht es definitiv Sinn auf einen objektbasierten State zu wechseln, um den Formularstatus elegant zu verwalten.

Die Hilfsfunktion und den Hilfszustandswert für die Simulation des Absenden von Formular belassen wir weiterhin. So können wir nach dem Klick auf “Absenden” irgendeine Aktion ausführen. In diesem Fall einfach die Werte aus dem Formular anzeigen, die übermittelt werden würden.

Hinweis

Ich verwende bei meinen Projekten immer die Namenskonvention field{Fieldname} oder field_{fieldname}. Sowohl in Angular, als auch in React, als auch in sonstigen Technologien, die mit HTML-Formularen arbeiten. Hier versuche ich mich soweit es geht an das Prinzip “Explizit ist besser als implizit” zu halten. Mit dem Zusatz field (oder field_) ist mir in jedem Kontext, bei jedem Framework und an jeder Stelle immer gleich klar, dass es sich hierbei um ein Formularfeld handelt. (Bspw. könnte email auch aus einem anderen Kontext kommen wie eigene State-Verwaltung oder einem anderen, entpackten Objekt, etc.)

Jeder wählt hier für ihn passenden Weg.

TypeScript StateExample.jsx
import { useState } from 'react';

import './MultiFieldForm.scss';

const MultiFieldForm = () => {
    const [formData, setFormData] = useState({
        fieldFirstname: '',
        fieldLastname: '',
        fieldEmail: '',
        fieldAge: ''
    });

    const [formSubmitted, setFormSubmitted] = useState(false);

    const handleInputChange = (event) => {
        const { name, value } = event.target;
        setFormData({
            ...formData,
            [name]: value
        });
    };

    const handleSubmit = (event) => {
        event.preventDefault();
        setFormSubmitted(currentState => true);
    };

    return (
        <>
            <form>
                <div className="form_field">
                    <label htmlFor="fieldFirstname">Vorname</label>
                    <input
                        type="text"
                        id="fieldFirstname"
                        name="fieldFirstname"
                        value={formData.fieldFirstname}
                        onChange={handleInputChange}
                    />
                </div>
                <div className="form_field">
                    <label htmlFor="fieldLastname">Nachname</label>
                    <input
                        type="text"
                        id="fieldLastname"
                        name="fieldLastname"
                        value={formData.fieldLastname}
                        onChange={handleInputChange}
                    />
                </div>
                <div className="form_field">
                    <label htmlFor="fieldEmail">E-Mail</label>
                    <input
                        type="email"
                        id="fieldEmail"
                        name="fieldEmail"
                        value={formData.fieldEmail}
                        onChange={handleInputChange}
                    />
                </div>
                <div className="form_field">
                    <label htmlFor="fieldAge">Alter</label>
                    <input
                        type="number"
                        id="fieldAge"
                        name="fieldAge"
                        value={formData.fieldAge}
                        onChange={handleInputChange}
                    />
                </div>
                <div className="form_actions">
                    <button onClick={handleSubmit}>Absenden</button>
                </div>
            </form>

            {formSubmitted && (
                <div className="submit_result">
                    <p><strong>Vorname:</strong> {formData.fieldFirstname}</p>
                    <p><strong>Nachname:</strong> {formData.fieldLastname}</p>
                    <p><strong>E-Mail:</strong> {formData.fieldEmail}</p>
                    <p><strong>Alter:</strong> {formData.fieldAge}</p>
                </div>
            )}
        </>
    );
};

export default MultiFieldForm;

In diesem Beispiel wird ein State-Objekt eingesetzt. Die Aktualisierung der Felder erfolgt in diesem Beispiel anhand der Feldnamen. Daher können wir die Funktion für die Aktualisierung der Werte onChange generisch halten.

JavaScript Beispiel
const handleInputChange = (event) => {
    const { name, value } = event.target;
    setFormData({
        ...formData,
        [name]: value
    });
};

Im [name] steht unser Feldname, beispielsweise fieldLastname. Im value - entsprechend der Wert. All unsere Formulardaten sind stets im Objekt formData gebündelt und können weiter verwendet werden.

React State - Formular mit mehreren Feldern - Beispiel

JavaScript Exkurs - Objekt Modifikation

Ich möchte noch etwas genauer auf das verwendete Konstrukt ...formData und [name]: value eingehen und kurz erklären, was hier das Verhalten ist und wie man das rekonstruieren kann.

Die ... Punkte werden verwendet, um das Objekt zu kopieren. Im Grunde erzeugen wir ein neues Objekt auf Basis bereits vorhandenem Objekt.

JavaScript Beispiel
const person = {
    name: 'John',
    job: 'Developer',
    salary: 50000
};

// Kopie erstellen
const personTwo = { ...person };

Hier haben wir einfach ein Objekt person erzeugt und davon eine Kopie erstellt und der Variable personTwo zugewiesen.

JavaScript - Objekt Kopie

Dies sind zwei unterschiedliche Objekte, was wir durch einen Vergleich einfach beweisen können.

JavaScript - Objekt Vergleich

TypeScript Objekt Vergleich
console.log(person === personTwo);
Output
false

Wir können es aber dadurch zusätzlich beweisen, indem wir das zweite Objekt ändern.

TypeScript Objekt Vergleich
// Eigenschaft bei personTwo ändern
personTwo.job = 'Mobile Developer';

// Beide Objekte ausgeben
console.log(person);
console.log(personTwo);
Output
{ name: 'John', job: 'Developer', salary: 50000 }
{ name: 'John', job: 'Mobile Developer', salary: 50000 }

JavaScript - Objekt Modifikation

Nun werden wir das Kopieren des Objekts und die Modifikation einer Eigenschaft am Objekt kombinieren. Hierzu verwenden wir personThree als Variable.

JavaScript Beispiel
const person = {
    name: 'John',
    job: 'Developer',
    salary: 50000
};

// Kopie erstellen & Eigenschaft ändern
const personThree = { ...person, job: 'Manager' };

Hier haben wir nun eine Kopie von person erstellt, das neue Objekt in der Variable personThree gespeichert und die Eigenschaft job am Objekt personThree modifiert. In diesem Fall haben wir es in einem Schritt getan. So, wie wir es auch in unserem Formular-Beispiel in React verwendet haben.

JavaScript - Objekt Kopie und Modifikation

Nun schauen wir uns [name] Schreibweise bzw. Verwendung an.

Info

Die Notation [name]: value nutzt eine besondere JavaScript-Funktion. Wenn man einen Variablennamen in [] eckige Klammern setzt, wird der Wert dieser Variable als Eigenschaftsname verwendet. Schauen wir uns das Ganze an einem Beispiel an, damit es klarer wird. Für diesen Zweck führen wir eine neue Variable personFour ein. Zusätzlich werden wir den Namen der Eigenschaft, welche wir am neuen Objekt ändern möchten, in einer Variable speichern. Diese Variable verwenden wir mit der oben beschrieben Notation.

JavaScript Beispiel
const person = {
    name: 'John',
    job: 'Developer',
    salary: 50000
};

// Variable definieren
const fieldName = 'job';

// Kopie erstellen & Eigenschaft ändern
const personFour = { ...person, [fieldName]: 'Tester' };

Wir haben hier nicht den Eigenschaftsnamen direkt angegeben, sondern eine Variable in eckige Klammern gesetzt. Wie oben in der Regel für die Notation beschrieben, wird der Wert dieser Variablen zum Namen der Eigenschaft am neuen Objekt.

JavaScript - Objekt Kopie und Variable als Feldname

Im Grunde können wir das Ganze auch so schreiben, auch wenn es nicht viel Sinn macht, aber technisch das Gleiche bedeutet.

TypeScript Alternative Verwendung
const person = {
    name: 'John',
    job: 'Developer',
    salary: 50000
};

const personFive = { ...person, ['job']: 'Game Developer' };

console.log(person);
console.log(personFive);
Output
{ name: 'John', job: 'Developer', salary: 50000 }
{ name: 'John', job: 'Game Developer', salary: 50000 }

Auch könnte man eine schlaue Funktion definieren, die uns den Feldnamen liefert. Hier, zur Demonstrationszwecken, ist die Funktion sehr einfach und nutzlos.

TypeScript Funktion als Generator
const person = {
    name: 'John',
    job: 'Developer',
    salary: 50000
};

// Funktion, die den oder die
// Eigenschaftsnamen generiert/zurückgibt
// [!code highlight]
function getFieldName() {
// [!code highlight]
    return 'job';
// [!code highlight]
}

const personSix = { ...person, [getFieldName()]: 'Security Officer' };

console.log(person);
console.log(personSix);
Output
{ name: 'John', job: 'Developer', salary: 50000 }
{ name: 'John', job: 'Security Officer', salary: 50000 }

Formular mit Validierung

Im dritten Beispiel schauen wir uns an, wie man Validierung in Verbindung mit useState() aufbauen könnte.

TypeScript FormValidation.jsx
import { useState } from 'react';

import './FormValidation.scss';

const FormValidation = () => {

    // State für Formulardaten
    const [formData, setFormData] = useState({
        fieldUsername: '',
        fieldEmail: '',
        fieldPassword: '',
        fieldPasswordConfirm: ''
    });

    // State für Validierungsfehler
    const [formErrors, setFormErrors] = useState({
        username: '',
        email: '',
        password: '',
        passwordConfirm: ''
    });

    // State für das Absenden des Formulars
    const [formSubmitted, setFormSubmitted] = useState(false);

    const validateEmail = (email) => {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
    };

    const handleInputChange = (event) => {
        const { name, value } = event.target;
        setFormData({ ...formData, [name]: value });
        validateField(name, value);
    };

    const validateField = (name, value) => {
        let errorMessage = '';

        switch (name) {
            case 'fieldUsername':
                if (value.trim() === '') {
                    errorMessage = 'Benutzername ist erforderlich';
                } else if (value.length < 3) {
                    errorMessage = 'Benutzername muss mindestens 3 Zeichen lang sein';
                }
                break;

            case 'fieldEmail':
                if (value.trim() === '') {
                    errorMessage = 'E-Mail ist erforderlich';
                } else if (!validateEmail(value)) {
                    errorMessage = 'E-Mail ist ungültig';
                }
                break;
            
            case 'fieldPassword':
                if (value.trim() === '') {
                    errorMessage = 'Passwort ist erforderlich';
                } else if (value.length < 6) {
                    errorMessage = 'Password muss mindestens 6 Zeichen lang sein';
                }
                
                if (formData.fieldPasswordConfirm && value !== formData.fieldPasswordConfirm) {
                    setFormErrors(prevErrors => ({
                        ...prevErrors,
                        fieldPasswordConfirm: ''
                    }));
                } else if (formData.fieldPasswordConfirm) {
                    setFormErrors(prevErrors => ({
                        ...prevErrors,
                        fieldPasswordConfirm: ''
                    }));
                }
                break;

            case 'fieldPasswordConfirm':
                if (value.trim() === '') {
                    errorMessage = 'Passwortbestätigung ist erforderlich';
                } else if (value !== formData.fieldPassword) {
                    errorMessage = 'Passwörter müssen übereinstimmen';
                }
                break;

            default:
                break;
        }

        setFormErrors(prevErrors => ({
            ...prevErrors,
            [name]: errorMessage
        }));

        return errorMessage === '';
    };

    const validateForm = () => {
        let isValid = true;

        Object.keys(formData).forEach(fieldName => {
            const fieldValue = formData[fieldName];
            const fieldIsValid = validateField(fieldName, fieldValue);

            if (!fieldIsValid) isValid = false;
        });

        return isValid;
    };

    const handleSubmit = (event) => {
        event.preventDefault();

        const isValid = validateForm();
        if (isValid) {
            setFormSubmitted(true);
        }
    };

    const getPasswortOutput = () => {
        return formData.fieldPassword.split("").map(() => "*").join("");
    };

    return (
        <>
            <form>
                <div className="form_field">
                    <label htmlFor="fieldUsername">Benutzername:</label>
                    <input
                        type="text"
                        id="fieldUsername"
                        name="fieldUsername"
                        value={formData.fieldUsername}
                        onChange={handleInputChange}
                    />

                    {formErrors.fieldUsername && (
                        <p className="error">{formErrors.fieldUsername}</p>
                    )}
                </div>
                <div className="form_field">
                    <label htmlFor="fieldEmail">E-Mail:</label>
                    <input
                        type="email"
                        id="fieldEmail"
                        name="fieldEmail"
                        value={formData.fieldEmail}
                        onChange={handleInputChange}
                    />

                    {formErrors.fieldEmail && (
                        <p className="error">{formErrors.fieldEmail}</p>
                    )}
                </div>
                <div className="form_field">
                    <label htmlFor="fieldPassword">Passwort:</label>
                    <input
                        type="password"
                        id="fieldPassword"
                        name="fieldPassword"
                        value={formData.fieldPassword}
                        onChange={handleInputChange}
                    />

                    {formErrors.fieldPassword && (
                        <p className="error">{formErrors.fieldPassword}</p>
                    )}
                </div>
                <div className="form_field">
                    <label htmlFor="fieldPasswordConfirm">Passwort (Wdh.):</label>
                    <input
                        type="password"
                        id="fieldPasswordConfirm"
                        name="fieldPasswordConfirm"
                        value={formData.fieldPasswordConfirm}
                        onChange={handleInputChange}
                    />

                    {formErrors.fieldPasswordConfirm && (
                        <p className="error">{formErrors.fieldPasswordConfirm}</p>
                    )}
                </div>
                <div className="form_actions">
                    <button onClick={handleSubmit}>
                        Absenden
                    </button>
                </div>
            </form>

            {formSubmitted && (
                <>
                    <p>Registrierung abgeschlossen</p>
                    <p>Benutzername: {formData.fieldUsername}</p>
                    <p>E-Mail: {formData.fieldEmail}</p>
                    <p>Passwort: {getPasswortOutput()}</p>
                </>
            )}
        </>
    );

};

export default FormValidation;

In diesem, etwas größeren Beispiel, haben wir eine simple Validierung der einzelnen Felder eingebaut. Sicherlich kann man die Validierungslogik und den Umfang etwas erhöhen. Der aktuelle Stand reicht allerdings aus, um das Prinzip zu verstehen.

React State - Beispiel eines Formulars mit Validierung

Formular mit bedingten Feldern

Im letzten Beispiel in diesem Artikel schauen wir uns, wie man beispielhaft ein Formular mit bedingten Feldern aufbauen kann.

Auch in diesem Formular werden wir Zustandswerte für die Formulardaten und potenzielle Fehler. Außerdem werden wir hier ein paar Validierungen durchführen und ein paar generische Funktionen für die Abfertigung von Eingabefeldern und Checkboxen haben.

TypeScript FormValidation.jsx
import { useState } from 'react';

import './ConditionalForm.scss';

const ConditionalForm = () => {
    const [formData, setFormData] = useState({
        fieldName: '',
        fieldEmail: '',
        fieldNotificationType: '',
        fieldPhone: '',
        fieldFrequency: '',
        fieldTopics: [],
        fieldCustomTopic: '',
        fieldTermsAccepted: false
    });

    const [fieldErrors, setFieldErrors] = useState({});

    const [formSubmitted, setFormSubmitted] = useState(false);

    const handleInputChange = (event) => {
        const { name, value } = event.target;
        setFormData({ ...formData, [name]: value });

        // Remove errors, when the field is edited
        if (fieldErrors[name]) {
            setFieldErrors({ ...fieldErrors, [name]: '' });
        }
    };

    // Checkbox changes
    const handleCheckboxChange = (event) => {
        const { name, checked } = event.target;
        setFormData({ ...formData, [name]: checked });

        // Remove errors, when the field is edited
        if (fieldErrors[name]) {
            setFieldErrors({ ...fieldErrors, [name]: '' });
        }
    };

    // Topics selection changes
    const handleTopicChange = (event) => {
        const { value, checked } = event.target;

        let updatedTopics;
        if (checked) {
            // Add element
            updatedTopics = [...formData.fieldTopics, value];
        } else {
            // Remove element
            updatedTopics = formData.fieldTopics.filter(topic => topic !== value);
        }

        setFormData({
            ...formData,
            fieldTopics: updatedTopics,
            fieldCustomTopic: value === 'other' && !checked ? '' : formData.fieldCustomTopic
        });

        // Remove errors, when the field is edited
        if (fieldErrors.fieldTopics) {
            setFieldErrors({ ...fieldErrors, fieldTopics: '' });
        }
    };

    const handleSubmit = (event) => {
        event.preventDefault();
        const newErrors = {};

        // Validate: fieldName
        if (!formData.fieldName.trim()) {
            newErrors.fieldName = 'Name ist erforderlich';
        }

        // Validate: fieldEmail
        if (!formData.fieldEmail.trim()) {
            newErrors.fieldEmail = 'E-Mail ist erforderlich';
        } else if (!/\S+@\S+\.\S+/.test(formData.fieldEmail)) {
            newErrors.fieldEmail = 'Ungültige E-Mail';
        }

        // Validate: fieldNotificationType (sms)
        if (formData.fieldNotificationType === 'sms' && !formData.fieldPhone) {
            newErrors.fieldPhone = 'Telefonnummer ist erforderlich für SMS-Benachrichtigungen';
        }

        // Validate: fieldNotificationType (none)
        if (formData.fieldNotificationType !== 'none' && !formData.fieldFrequency) {
            newErrors.fieldFrequency = 'Bitte wähle eine Benachrichtigungshäufigkeit';
        }

        // Validate: fieldTopics (length)
        if (formData.fieldTopics.length === 0) {
            newErrors.fieldTopics = 'Bitte wähle mindestens ein Thema';
        }

        // Validate: fieldTopics (other)
        if (formData.fieldTopics.includes('other') && !formData.fieldCustomTopic.trim()) {
            newErrors.fieldCustomTopic = 'Bitte gib ein benutzerdefiniertes Thema an';
        }

        // Validate: fieldTermsAccepted
        if (!formData.fieldTermsAccepted) {
            newErrors.fieldTermsAccepted = 'Du musst den Bedingungen zustimmen';
        }

        // Submit form or set errors
        if (Object.keys(newErrors).length > 0) {
            setFieldErrors(newErrors);
        } else {
            setFormSubmitted(true);
        }
    };

    return (
        <>
            <form>

                {/* FIELD: fieldName (name) */}
                <div className="form_field">
                    <label htmlFor="fieldName">Name</label>
                    <input
                        type="text"
                        id="fieldName"
                        name="fieldName"
                        value={formData.fieldName}
                        onChange={handleInputChange}
                    />

                    {fieldErrors.fieldName && (
                        <p className="field_error">{fieldErrors.fieldName}</p>
                    )}
                </div>

                {/* FIELD: fieldEmail (email) */}
                <div className="form_field">
                    <label htmlFor="fieldEmail">E-Mail</label>
                    <input
                        type="email"
                        id="fieldEmail"
                        name="fieldEmail"
                        value={formData.fieldEmail}
                        onChange={handleInputChange}
                    />

                    {fieldErrors.fieldEmail && (
                        <p className="field_error">{fieldErrors.fieldEmail}</p>
                    )}
                </div>

                {/* FIELD: fieldNotificationType (notificationType) */}
                <div className="form_field">
                    <label htmlFor="fieldNotificationType">Benachrichtigungsart</label>
                    <select
                        name="fieldNotificationType"
                        value={formData.fieldNotificationType}
                        onChange={handleInputChange}
                    >
                        <option value="">Bitte wählen</option>
                        <option value="email">E-Mail</option>
                        <option value="sms">SMS</option>
                        <option value="none">Keine Benachrichtigung</option>
                    </select>

                    {fieldErrors.fieldNotificationType && (
                        <p className="field_error">{fieldErrors.fieldNotificationType}</p>
                    )}
                </div>

                {/* FIELD OPTIONAL: fieldPhone (phone) */}
                {formData.fieldNotificationType === 'sms' && (
                    <div className="form_field">
                        <label htmlFor="fieldPhone">Telefon</label>
                        <input
                            type="tel"
                            id="fieldPhone"
                            name="fieldPhone"
                            value={formData.fieldPhone}
                            onChange={handleInputChange}
                        />

                        {fieldErrors.fieldPhone && (
                            <p className="field_error">{fieldErrors.fieldPhone}</p>
                        )}
                    </div>
                )}

                {/* FIELD OPTIONAL: fieldFrequency (frequency) */}
                {formData.fieldNotificationType !== '' && formData.fieldNotificationType !== 'none' && (
                    <div className="form_field">
                        <label htmlFor="fieldFrequency">Benachrichtigungshäufigkeit</label>
                        <select
                            id="fieldFrequency"
                            name="fieldFrequency"
                            value={formData.fieldFrequency}
                            onChange={handleInputChange}
                        >
                            <option value="">Bitte wählen</option>
                            <option value="daily">Täglich</option>
                            <option value="weekly">Wöchentlich</option>
                            <option value="monthly">Monatlich</option>
                        </select>

                        {fieldErrors.fieldFrequency && (
                            <p className="field_error">{fieldErrors.fieldFrequency}</p>
                        )}
                    </div>
                )}

                {/* FIELD: fieldTopics (topics) */}
                <div className="form_field display_column">
                    <label>Interessante Themen</label>
                    <div className="checkbox_wrapper">
                        <input
                            type="checkbox"
                            id="topic_news"
                            value="news"
                            checked={formData.fieldTopics.includes("news")}
                            onChange={handleTopicChange}
                        />
                        <label htmlFor="topic_news">Nachrichten</label>
                    </div>
                    <div className="checkbox_wrapper">
                        <input
                            type="checkbox"
                            id="topic_updates"
                            value="updates"
                            checked={formData.fieldTopics.includes("updates")}
                            onChange={handleTopicChange}
                        />
                        <label htmlFor="topic_updates">Produkt Updates</label>
                    </div>
                    <div className="checkbox_wrapper">
                        <input
                            type="checkbox"
                            id="topic_events"
                            value="events"
                            checked={formData.fieldTopics.includes("events")}
                            onChange={handleTopicChange}
                        />
                        <label htmlFor="topic_events">Veranstaltungen</label>
                    </div>
                    <div className="checkbox_wrapper">
                        <input
                            type="checkbox"
                            id="topic_other"
                            value="other"
                            checked={formData.fieldTopics.includes("other")}
                            onChange={handleTopicChange}
                        />
                        <label htmlFor="topic_other">Eigenes Thema</label>
                    </div>

                    {fieldErrors.fieldTopics && (
                        <p className="field_error">{fieldErrors.fieldTopics}</p>
                    )}
                </div>

                {/* FIELD OPTIONAL: fieldCustomTopic (custom topic) */}
                {formData.fieldTopics.includes('other') && (
                    <div className="form_field">
                        <label htmlFor="fieldCustomTopic">Eigenes Thema</label>
                        <input
                            type="text"
                            id="fieldCustomTopic"
                            name="fieldCustomTopic"
                            value={formData.fieldCustomTopic}
                            onChange={handleInputChange}
                        />

                        {fieldErrors.fieldCustomTopic && (
                            <p className="field_error">{fieldErrors.fieldCustomTopic}</p>
                        )}
                    </div>
                )}

                {/* FIELD: fieldTermsAccepted (terms) */}
                <div className="form_field">
                    <div className="checkbox_wrapper">
                        <input
                            type="checkbox"
                            id="fieldTermsAccepted"
                            name="fieldTermsAccepted"
                            checked={formData.fieldTermsAccepted}
                            onChange={handleCheckboxChange}
                        />
                        <label htmlFor="fieldTermsAccepted">Ich stimme den Nutzungsbedingungen zu</label>
                    </div>

                    {fieldErrors.fieldTermsAccepted && (
                        <p className="field_error">{fieldErrors.fieldTermsAccepted}</p>
                    )}
                </div>

                <div className="form_actions">
                    <button onClick={handleSubmit}>
                        Speichern
                    </button>
                </div>
            </form>

            {formSubmitted && (
                <div className="form_submitted_info">
                    <p>Hallo, {formData.fieldName}, die Einstellungen wurden gespeichert.</p>
                </div>
            )}
        </>
    );
};

export default ConditionalForm;

React State - Beispiel eines Formulars mit bedingten Feldern

/ Weiter

Zurück zu State

Zur Übersicht