Zurück zu blog
26. Feb. 2025
4 Minuten Lesezeit

React - Forms & Benutzer Eingaben

Betrachtung von Forms und einzelner Benutzereingaben in React und der Validierung

Theorie

Eine Form ist eine Sammlung von Eingabefelder

  • Form

    • Werte können mit State verwaltet werden
    • Alternative Extration mit ref oder mit FormData und nativen Browser Funktionen
  • Validierung

    • kann sehr schwierig sein aufgrund des Triggers
    • keystroke kann zu verfrühten Fehler führen
    • lost focus eventuell eine zu lange Fehlermeldung
    • mit form Bestätigung eventuell zu spät

Beispiel für eine Form

//Login.jsx
export default function Login() {
  function handleSubmit() {
    event.preventDefault();
    console.log('Submitted!');
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2>Login</h2>
      <div className="control-row">
        <div className="control no-margin">
          <label htmlFor="email">Email</label>
          <input id="email" type="email" name="email" />
        </div>
        <div className="control no-margin">
          <label htmlFor="password">Password</label>
          <input id="password" type="password" name="password" />
        </div>
      </div>
      <p className="form-actions">
        <button className="button button-flat">Reset</button>
        <button  className="button" >Login</button>
      </p>
    </form>
  );
}

Generischer Handler für Benutzer Eingaben

import { useState } from "react";

export default function Login() {

  const [enteredValues, setEnteredValues] = useState({
    email: '',
    password: ''
  });

  function handleSubmit() {
    event.preventDefault();
    console.log(enteredValues);
  }

  function handleInputChange(event, identifier) {
    setEnteredValues(prevValues => ({
      ...prevValues,
      [identifier]: event.target.value
    }))
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2>Login</h2>
      <div className="control-row">
        <div className="control no-margin">
          <label htmlFor="email">Email</label>
          <input id="email" type="email" name="email" onChange={(event) => handleInputChange(event,'email')} value={enteredValues.email} />
        </div>
        <div className="control no-margin">
          <label htmlFor="password">Password</label>
          <input id="password" type="password" name="password" onChange={(event) => handleInputChange(event,'password')} value={enteredValues.password}/>
        </div>
      </div>
      <p className="form-actions">
        <button className="button button-flat">Reset</button>
        <button className="button" >Login</button>
      </p>
    </form>
  );
}

Komplexere Forms mithilfe von FormData (native Browser Unterstützung)

  • Checkboxen (mehrere Werte) müssen mit getAll geholt werden, nicht in fromEntries inbegriffen
//Signup.jsx
export default function Signup() {

    function handleSubmit(event) {
        event.preventDefault();
        const fd = new FormData(event.target);
        const acquisitionChannel = fd.getAll('acquisition');
        const data = Object.fromEntries(fd.entries());
        data.acquisition = acquisitionChannel;
        console.log(data);
    }
    return (
        <form onSubmit={handleSubmit}>
            <h2>Welcome on board!</h2>
            <p>We just need a little bit of data from you to get you started 🚀</p>
            <div className="control">
                <label htmlFor="email">Email</label>
                <input id="email" type="email" name="email" />
            </div>
            <div className="control-row">
                <div className="control">
                    <label htmlFor="password">Password</label>
                    <input id="password" type="password" name="password" />
                </div>
                <div className="control">
                    <label htmlFor="confirm-password">Confirm Password</label>
                    <input
                        id="confirm-password"
                        type="password"
                        name="confirm-password"
                    />
                </div>
            </div>
            <hr />
            <div className="control-row">
                <div className="control">
                    <label htmlFor="first-name">First Name</label>
                    <input type="text" id="first-name" name="first-name" />
                </div>
                <div className="control">
                    <label htmlFor="last-name">Last Name</label>
                    <input type="text" id="last-name" name="last-name" />
                </div>
            </div>
            <div className="control">
                <label htmlFor="phone">What best describes your role?</label>
                <select id="role" name="role">
                    <option value="student">Student</option>
                    <option value="teacher">Teacher</option>
                    <option value="employee">Employee</option>
                    <option value="founder">Founder</option>
                    <option value="other">Other</option>
                </select>
            </div>
            <fieldset>
                <legend>How did you find us?</legend>
                <div className="control">
                    <input
                        type="checkbox"
                        id="google"
                        name="acquisition"
                        value="google"
                    />
                    <label htmlFor="google">Google</label>
                </div>
                <div className="control">
                    <input
                        type="checkbox"
                        id="friend"
                        name="acquisition"
                        value="friend"
                    />
                    <label htmlFor="friend">Referred by friend</label>
                </div>
                <div className="control">
                    <input type="checkbox" id="other" name="acquisition" value="other" />
                    <label htmlFor="other">Other</label>
                </div>
            </fieldset>
            <div className="control">
                <label htmlFor="terms-and-conditions">
                    <input type="checkbox" id="terms-and-conditions" name="terms" />I
                    agree to the terms and conditions
                </label>
            </div>
            <p className="form-actions">
                <button type="reset" className="button button-flat">
                    Reset
                </button>
                <button type="submit" className="button">
                    Sign up
                </button>
            </p>
        </form>
    );
}

Zurücksetzen von Werten

//Signup.jsx
   function handleSubmit(event) {
        event.preventDefault();
        const fd = new FormData(event.target);
        const acquisitionChannel = fd.getAll('acquisition');
        const data = Object.fromEntries(fd.entries());
        data.acquisition = acquisitionChannel;
        console.log(data);
        event.target.reset();
    }
   <form onSubmit={handleSubmit}>
      <p className="form-actions">
                <button type="reset" className="button button-flat">
                    Reset
                </button>
                <button type="submit" className="button">
                    Sign up
                </button>
            </p>
</form>

Einzelnde EingabeFelder

  • Validierung nach dem Eintippen

    • geht nur mit einem State Ansatz
  • Validierung mit Built-in Props

    • required

Wiederverwendbare Komponente

//Input.jsx
export default function Input({ label, id, error, ...props }) {
    return (
        <div className="control no-margin">
            <label htmlFor={id}>Email</label>
            <input
                id={id}
                {...props}
            />
            <div className="control-error">{error && <p>{error}</p>}</div>
        </div>
    );
}
//StateLogin.jsx
 <Input
                    label="Email"
                    id="email"
                    type="email"
                    name="email"
                    onChange={(event) => handleInputChange(event, 'email')}
                    value={enteredValues.email}
                    onBlur={() => handleInputBlur('email')}
                    error={emailIsInvalid && 'Please enter a valid email!'}
                />

Auslagerung Validierung

//validation.js
export function isEmail(value) {
  return value.includes('@');
}

export function isNotEmpty(value) {
  return value.trim() !== '';
}

export function hasMinLength(value, minLength) {
  return value.length >= minLength;
}

export function isEqualsToOtherValue(value, otherValue) {
  return value === otherValue;
}

Eigenen Hook für Validerung erstellen

//useInput.js
import { useState } from "react";

export function useInput(defaultValue, validationFn) {
    const [enteredValue, setEnteredValue] = useState(defaultValue);
    const [didEdit, setDidEdit] = useState(false);
    const valueIsValid = validationFn(enteredValue);

    function handleInputChange(event) {
        setEnteredValue(event.target.value);
        setDidEdit(false);
    }

    function handleInputBlur() {
        setDidEdit(true);
    }

    return {
        value: enteredValue,
        handleInputChange,
        handleInputBlur,
        hasError: didEdit && !valueIsValid
    };
}
//StateLogin.jsx
import Input from "./Input";
import { isEmail, isNotEmpty, hasMinLength } from '../util/validation.js'
import { useInput } from "../hooks/useInput.js";

export default function Login() {

    //const {value: emailValue, handleInputChange: handleEmailChange, handleInputBlur: handleEmailBlur} = useInput('', isEmail);

    const {
        value: emailValue,
        handleInputChange: handleEmailChange,
        handleInputBlur: handleEmailBlur,
        hasError: emailHasError
    } = useInput('', (value) => isEmail(value) && isNotEmpty(value));
    const {
        value: passwordValue,
        handleInputChange: handlePasswordChange,
        handleInputBlur: handlePasswordBlur,
        hasError: passwordHasError
    } = useInput('', (value) => hasMinLength(value, 6));

      function handleSubmit() {
        event.preventDefault();
        if (emailHasError || passwordHasError) {
            return;
        }
        console.log(emailValue, passwordValue);
        }

    return (
        <form onSubmit={handleSubmit}>
            <h2>Login</h2>
            <div className="control-row">
                <Input
                    label="Email"
                    id="email"
                    type="email"
                    name="email"
                    onChange={handleEmailChange}
                    value={emailValue}
                    onBlur={handleEmailBlur}
                    error={emailHasError && 'Please enter a valid email!'}
                />
                <Input
                    label="Password"
                    id="password"
                    type="password"
                    name="password"
                    onChange={handlePasswordChange}
                    value={passwordValue}
                    onBlur={handlePasswordBlur}
                    error={passwordHasError && 'Please enter a valid password!'}
                />
            </div>
            <p className="form-actions">
                <button className="button button-flat">Reset</button>
                <button className="button" >Login</button>
            </p>
        </form>
    );
}