navigation Navigation


Formulare und useState()


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.

Inhaltsverzeichnis

    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.

    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.


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

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


    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.

    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.

    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.

    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

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

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

    Objekt Vergleich
    // Eigenschaft bei personTwo ändern
    personTwo.job = 'Mobile Developer';
    
    // Beide Objekte ausgeben
    console.log(person);
    console.log(personTwo);
    { 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.

    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.

    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.

    Alternative Verwendung
    const person = {
        name: 'John',
        job: 'Developer',
        salary: 50000
    };
    
    const personFive = { ...person, ['job']: 'Game Developer' };
    
    console.log(person);
    console.log(personFive);
    { 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.

    Funktion als Generator
    const person = {
        name: 'John',
        job: 'Developer',
        salary: 50000
    };
    
    // Funktion, die den oder die
    // Eigenschaftsnamen generiert/zurückgibt
    function getFieldName() {
        return 'job';
    }
    
    const personSix = { ...person, [getFieldName()]: 'Security Officer' };
    
    console.log(person);
    console.log(personSix);
    { 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.

    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.

    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