Formik zur Verwaltung von Formularen in React verwenden

Avatar of Adebiyi Adedotun
Adebiyi Adedotun am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Es besteht kein Zweifel daran, dass Webformulare eine integrale Rolle in unseren Websites oder Anwendungen spielen. Standardmäßig bieten sie eine nützliche Sammlung von Elementen und Funktionen – von Legenden und Feldgruppen bis hin zu nativem Validierungs- und Statusmanagement – aber sie bringen uns nur bis zu einem gewissen Punkt, wenn wir die Besonderheiten ihrer Verwendung berücksichtigen. Wie können wir beispielsweise den Zustand eines Formulars manipulieren? Was ist mit verschiedenen Formen der Validierung? Selbst das Verbinden eines Formulars mit Post-Übermittlungen ist manchmal eine entmutigende Aufgabe.

Komponentenbasierte Front-End-Bibliotheken wie React können die Aufgabe der Verknüpfung von Webformularen erleichtern, aber auch umständlich und redundant werden. Deshalb möchte ich Ihnen Formik vorstellen, eine kleine Bibliothek, die die drei nervigsten Teile des Schreibens von Formularen in React löst:

  1. Zustandsmanipulation
  2. Formularvalidierung (und Fehlermeldungen)
  3. Formularübermittlung

Wir werden in diesem Beitrag gemeinsam ein Formular erstellen. Wir beginnen mit einer React-Komponente und integrieren dann Formik, während wir die Art und Weise demonstrieren, wie es Zustände, Validierung und Übermittlungen handhabt.

Erstellen einer Formular als React-Komponente

Komponenten leben und atmen durch ihren Zustand und ihre Props. Was HTML-Formularelemente mit React-Komponenten gemeinsam haben, ist, dass sie von Natur aus einen internen Zustand beibehalten. Ihre Werte werden auch automatisch in ihrem value-Attribut gespeichert.

Wenn Formularelemente ihren eigenen Zustand in React verwalten dürfen, werden sie zu uncontrolled Komponenten. Das ist nur eine schicke Art zu sagen, dass das DOM den Zustand statt React verwaltet. Und obwohl das funktioniert, ist es oft einfacher, controlled Komponenten zu verwenden, bei denen React den Zustand verwaltet und als einzige Quelle der Wahrheit dient, anstatt das DOM.

Die Markup für ein einfaches HTML-Formular könnte ungefähr so aussehen:

<form>
  <div className="formRow">
    <label htmlFor="email">Email address</label>
    <input type="email" name="email" className="email" />
  </div>
  <div className="formRow">
    <label htmlFor="password">Password</label>
    <input type="password" name="password" className="password" />
  </div>
  <button type="submit">Submit</button>
</form>

Das können wir wie folgt in eine kontrollierte React-Komponente umwandeln:

function HTMLForm() {
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");


  return (
    <form>
      <div className="formRow">
        <label htmlFor="email">Email address</label>
        <input
          type="email"
          name="email"
          className="email"
          value={email}
          onChange={e => setEmail(e.target.value)}
        />
      </div>
      <div className="formRow">
        <label htmlFor="password">Password</label>
        <input
          type="password"
          name="password"
          className="password"
          value={password}
          onChange={e => setPassword(e.target.value)}
        />
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

Das ist etwas umständlich, aber es hat einige Vorteile:

  1. Wir erhalten eine einzige Quelle der Wahrheit für Formularwerte im Zustand.
  2. Wir können das Formular validieren, wann und wie wir wollen.
  3. Wir erhalten Leistungsvorteile, indem wir das laden, was wir brauchen und wann wir es brauchen.

Okay, warum also noch einmal Formik?

Wie bei allem in JavaScript gibt es bereits eine Fülle von Formularverwaltungsbibliotheken wie React Hook Form und Redux Form, die wir verwenden können. Aber es gibt mehrere Dinge, die Formik aus der Masse hervorstechen lassen:

  1. Es ist deklarativ: Formik eliminiert Redundanz durch Abstraktion und übernimmt die Verantwortung für Zustand, Validierung und Übermittlungen.
  2. Es bietet einen Fluchtweg: Abstraktion ist gut, aber Formulare sind spezifisch für bestimmte Muster. Formik abstrahiert für Sie, lässt Sie aber auch die Kontrolle behalten, falls Sie dies benötigen.
  3. Es bringt Formularzustände zusammen: Formik behält alles, was mit Ihrem Formular zu tun hat, innerhalb Ihrer Formular-Komponenten.
  4. Es ist anpassungsfähig: Formik zwingt Ihnen keine Regeln auf. Sie können so wenig oder so viel Formik verwenden, wie Sie benötigen.
  5. Einfach zu bedienen: Formik funktioniert einfach.

Klingt gut? Lassen Sie uns Formik in unsere Formular-Komponente implementieren.

Formik verwenden

Wir werden ein einfaches Login-Formular erstellen, um uns mit den Grundlagen vertraut zu machen. Wir werden uns mit drei verschiedenen Möglichkeiten befassen, mit Formik zu arbeiten:

  1. Verwendung des useFormik Hooks
  2. Verwendung von Formik mit React Context
  3. Verwendung von withFormik als Higher-Order Component

Ich habe eine Demo mit den benötigten Paketen, Formik und Yup, erstellt.

Methode 1: Verwendung des useFormik-Hooks

Wie es jetzt ist, tut unser Formular nichts Greifbares. Um Formik zu verwenden, müssen wir den useFormik-Hook importieren. Wenn wir den Hook verwenden, gibt er uns alle Formik-Funktionen und Variablen zurück, die uns bei der Verwaltung des Formulars helfen. Wenn wir die zurückgegebenen Werte in der Konsole ausgeben, erhalten wir dies:

Showing console output of the various hooks and objects that are logged by Formik.

Wir rufen useFormik auf und übergeben ihm initialValues, um zu beginnen. Dann löst ein onSubmit-Handler eine Formularübermittlung aus. So sieht das aus:

// This is a React component
function BaseFormik() {
  const formik = useFormik({
    initialValues: {
      email: "",
      password: ""
    },
    onSubmit(values) {
      // This will run when the form is submitted
    }
  });
  
 // If you're curious, you can run this Effect
 //  useEffect(() => {
 //   console.log({formik});
 // }, [])


  return (
    // Your actual form
  )
}

Dann binden wir Formik an unsere Formular-Elemente:

// This is a React component
function BaseFormik() {
  const formik = useFormik({
    initialValues: {
      email: "",
      password: ""
    },
    onSubmit(values) {
      // This will run when the form is submitted
    }
  });
  
 // If you're curious, you can run this Effect
 //  useEffect(() => {
 //   console.log({formik});
 // }, [])


  return (
  // We bind "onSubmit" to "formik.handleSubmit"
  <form className="baseForm" onSubmit={formik.handleSubmit} noValidate>
    <input
      type="email"
      name="email"
      id="email"
      className="email formField"
      value={formik.values.email} // We also bind our email value
      onChange={formik.handleChange} // And, we bind our "onChange" event.
    />
  </form>
  )
}

So funktioniert die Bindung:

  1. Es behandelt die Formularübermittlung mit onSubmit={formik.handleSubmit}.
  2. Es verwaltet den Zustand der Eingaben mit value={formik.values.email} und onChange={formik.handleChange}.

Wenn Sie genauer hinschauen, mussten wir unseren Zustand nicht einrichten und auch nicht die onChange- oder onSubmit-Ereignisse handhaben, wie wir es typischerweise mit React tun würden.

Wie Sie jedoch vielleicht bemerkt haben, enthält unser Formular einige Redundanzen. Wir mussten Formik abfragen und manuell den value und das onChange-Ereignis des Formularfelds binden. Das bedeutet, wir sollten den zurückgegebenen Wert de-strukturieren und die notwendigen Props sofort an ein abhängiges Feld binden, wie folgt:

// This is a React component
function BaseFormik() {
  const {getFieldProps, handleSubmit} = useFormik({
    initialValues: {
      email: "",
      password: ""
    },
    onSubmit(values) {
      // This will run when the form is submitted
    }
  });
  
 // If you're curious, you can run this Effect
 //  useEffect(() => {
 //   console.log({formik});
 // }, [])


  return (
  <form className="baseForm" onSubmit={handleSubmit} noValidate>
    <input
      type="email"
      id="email"
      className="email formField"
      {...getFieldProps("email")} // We pass the name of the dependent field
    />
  </form>
  )
}

Lassen Sie uns die Dinge mit der enthaltenen <Formik/>-Komponente noch weiter treiben.

Methode 2: Verwendung von Formik mit React Context

Die <Formik/>-Komponente stellt verschiedene andere Komponenten bereit, die mehr Abstraktion und sinnvolle Standardwerte hinzufügen. Zum Beispiel sind Komponenten wie <Form/>, <Field/> und <ErrorMessage/> sofort einsatzbereit.

Beachten Sie, dass Sie diese Komponenten nicht verwenden müssen, wenn Sie mit <Formik/> arbeiten, aber sie erfordern <Formik/> (oder withFormik), wenn Sie sie verwenden.

Die Verwendung von <Formik/> erfordert eine Überarbeitung, da sie das Render Props-Pattern verwendet, im Gegensatz zu Hooks mit useFormik. Das Render Props-Pattern ist nichts Neues in React. Es ist ein Pattern, das die Wiederverwendbarkeit von Code zwischen Komponenten ermöglicht – etwas, das Hooks besser lösen. Dennoch hat <Formik/> eine ganze Reihe von benutzerdefinierten Komponenten, die die Arbeit mit Formularen erheblich erleichtern.

import { Formik } from "formik";


function FormikRenderProps() {
  const initialValues = {
    email: "",
    password: ""
  };
  function onSubmit(values) {
    // Do stuff here...
    alert(JSON.stringify(values, null, 2));
  }
  return (
      <Formik {...{ initialValues, onSubmit }}>
        {({ getFieldProps, handleSubmit }) => (
            <form className="baseForm" onSubmit={handleSubmit} noValidate>
              <input
                type="email"
                id="email"
                className="email formField"
                {...getFieldProps("email")}
              />
            </form>
        )}
      </Formik>
  );
}

Beachten Sie, dass initialValues und onSubmit vollständig von useFormik getrennt wurden. Das bedeutet, wir können die Props übergeben, die <Formik/> benötigt, insbesondere initialValues und useFormik.

<Formik/> gibt einen Wert zurück, der in getFieldProps und handleSubmit de-strukturiert wurde. Alles andere bleibt im Grunde gleich wie bei der ersten Methode mit useFormik.

Hier ist eine Auffrischung zu React Render Props, wenn Sie sich ein wenig rostig fühlen.

Wir haben bisher noch keine <Formik/>-Komponenten verwendet. Ich habe dies absichtlich getan, um die Anpassungsfähigkeit von Formik zu demonstrieren. Wir wollen diese Komponenten sicherlich für unsere Formularfelder verwenden, also schreiben wir die Komponente neu, sodass sie die <Form/>-Komponente verwendet.

import { Formik, Field, Form } from "formik";


function FormikRenderProps() {
  const initialValues = {
    email: "",
    password: ""
  };
  function onSubmit(values) {
    // Do stuff here...
    alert(JSON.stringify(values, null, 2));
  }
  return (
      <Formik {...{ initialValues, onSubmit }}>
        {() => (
            <Form className="baseForm" noValidate>
              <Field
                type="email"
                id="email"
                className="email formField"
                name="email"
              />
            </Form>
        )}
      </Formik>
  );
}

Wir haben <form/> durch <Form/> ersetzt und den onSubmit-Handler entfernt, da Formik das für uns übernimmt. Denken Sie daran, es übernimmt alle Verantwortlichkeiten für die Verwaltung von Formularen.

Wir haben auch <input/> durch <Field/> ersetzt und die Bindungen entfernt. Auch hier kümmert sich Formik darum.

Es besteht auch keine Notwendigkeit mehr, sich um den Rückgabewert von <Formik/> zu kümmern. Sie haben es erraten, Formik kümmert sich auch darum.

Formik kümmert sich um alles für uns. Wir können uns nun mehr auf die Geschäftslogik unserer Formulare konzentrieren, anstatt auf Dinge, die im Wesentlichen abstrahiert werden können.

Wir sind so gut wie fertig und raten Sie mal? Wir haben uns nicht um Zustandsverwaltung oder Formularübermittlungen gekümmert!

„Was ist mit Validierung?“, könnten Sie fragen. Wir haben uns damit nicht beschäftigt, weil es ein eigenes Level ist. Lassen Sie uns das behandeln, bevor wir zur letzten Methode übergehen.

Formularvalidierung mit Formik

Wenn Sie jemals mit Formularen gearbeitet haben (und ich wette, das haben Sie), wissen Sie, dass Validierung nichts ist, was man vernachlässigen sollte.

Wir wollen kontrollieren, *wann* und *wie* validiert wird, damit sich neue Möglichkeiten für bessere Benutzererfahrungen eröffnen. Gmail beispielsweise lässt Sie kein Passwort eingeben, es sei denn, die E-Mail-Adresseneingabe wurde validiert und authentifiziert. Wir könnten auch etwas tun, bei dem wir sofort validieren und Meldungen anzeigen, ohne zusätzliche Interaktionen oder Seitenaktualisierungen.

Hier sind drei Möglichkeiten, wie Formik die Validierung handhaben kann:

  1. Auf Formularebene
  2. Auf Feld-Ebene
  3. Mit manuellen Auslösern

Validierung auf Formularebene bedeutet, das Formular als Ganzes zu validieren. Da wir sofortigen Zugriff auf die Formularwerte haben, können wir das gesamte Formular auf einmal validieren, indem wir entweder

  • validate verwenden, oder
  • eine Drittanbieter-Bibliothek mit validationSchema verwenden.

Sowohl validate als auch validationSchema sind Funktionen, die ein errors-Objekt mit Schlüssel/Wert-Paaren zurückgeben, die denen von initialValues entsprechen. Wir können diese an  useFormik, <Formik/> oder withFormik übergeben. 

Während validate für benutzerdefinierte Validierungen verwendet wird, wird validationSchema mit einer Drittanbieter-Bibliothek wie Yup verwendet. 

Hier ist ein Beispiel mit validate:

// Pass the `onSubmit` function that gets called when the form is submitted.
const formik = useFormik({
  initialValues: {
    email: "",
    password: ""
  },
  // We've added a validate function
  validate() {
    const errors = {};
    // Add the touched to avoid the validator validating all fields at once
    if (formik.touched.email && !formik.values.email) {
      errors.email = "Required";
    } else if (
      !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(formik.values.email)
    ) {
      errors.email = "Invalid email address";
    }
    if (formik.touched.password && !formik.values.password) {
      errors.password = "Required";
    } else if (formik.values.password.length <= 8) {
      errors.password = "Must be more than 8 characters";
    }
    return errors;
  },
  onSubmit(values) {
    // Do stuff here...
  }
});
// ...

Und hier ein Beispiel mit validationSchema stattdessen:

const formik = useFormik({
  initialValues: {
    email: "",
    password: ""
  },
  // We used Yup here.
  validationSchema: Yup.object().shape({
    email: Yup.string()
      .email("Invalid email address")
      .required("Required"),
    password: Yup.string()
      .min(8, "Must be more than 8 characters")
      .required("Required")
  }),
  onSubmit(values) {
    // Do stuff here...
  }
});

Die Validierung auf Feld-Ebene oder die Verwendung von manuellen Auslösern ist relativ einfach zu verstehen. Obwohl Sie wahrscheinlich die meiste Zeit die Formular-Level-Validierung verwenden werden. Es lohnt sich auch, die Dokumentation zu prüfen, um andere Anwendungsfälle zu sehen.

Methode 3: Verwendung von withFormik als Higher-Order Component

withFormik ist eine Higher-Order Component und kann auf diese Weise verwendet werden, wenn das Ihr Ding ist. Schreiben Sie das Formular und stellen Sie es dann über Formik zur Verfügung.

Ein paar praktische Beispiele

Bisher haben wir uns mit Formik vertraut gemacht, die Vorteile der Verwendung für die Erstellung von Formularen in React behandelt und einige Methoden zur Implementierung als React-Komponente erläutert, während wir verschiedene Möglichkeiten zur Verwendung für die Validierung demonstriert haben. Was wir noch nicht getan haben, ist, uns Beispiele für diese Schlüsselkonzepte anzusehen.

Betrachten wir also ein paar praktische Anwendungen: das Anzeigen von Fehlermeldungen und das Generieren eines Benutzernamens basierend auf dem, was in das E-Mail-Feld eingegeben wurde.

Anzeigen von Fehlermeldungen

Wir haben unser Formular erstellt und validiert. Und wir haben einige Fehler abgefangen, die in unserem errors-Objekt gefunden werden können. Aber es ist nutzlos, wenn wir diese Fehler nicht tatsächlich anzeigen.

Formik macht dies zu einer ziemlich trivialen Aufgabe. Alles, was wir tun müssen, ist das errors-Objekt zu überprüfen, das von jeder der von uns betrachteten Methoden zurückgegeben wird – <Formik/>, useFormik oder withFormik – und es anzuzeigen.

<label className="formFieldLabel" htmlFor="email">
  Email address
  <span className="errorMessage">
    {touched["email"] && errors["email"]}
  </span>
</label>
<div className="formFieldWrapInner">
  <input
    type="email"
    id="email"
    className="email formField"
    {...getFieldProps("email")}
  />
</div>

Wenn während der Validierung ein Fehler auftritt, wird {touched["email"] && errors["email"]} ihn dem Benutzer anzeigen.

Wir könnten das Gleiche mit <ErrorMessage/> tun. Damit müssen wir ihm nur den Namen des abhängigen Feldes nennen, das beobachtet werden soll.

<ErrorMessage name="email">
  {errMsg => <span className="errorMessage">{errMsg}</span>}
</ErrorMessage>

Generieren eines Benutzernamens aus einer E-Mail-Adresse

Stellen Sie sich ein Formular vor, das automatisch einen Benutzernamen für Ihre Benutzer generiert, basierend auf ihrer E-Mail-Adresse. Mit anderen Worten, was auch immer der Benutzer in das E-Mail-Feld eingibt, wird extrahiert, das „@“-Zeichen und alles danach wird entfernt, und was übrig bleibt, ist ein Benutzername.

Beispiel: [email protected] erzeugt @jane.

Formik stellt Helfer bereit, die seine Funktionalität „abfangen“ und uns ermöglichen, einige Effekte auszuführen. Im Falle der automatischen Generierung eines Benutzernamens wäre eine Möglichkeit Formiks setValues.

onSubmit(values) {
  // We added a `username` value for the user which is everything before @ in their email address.
  setValues({
    ...values,
    username: `@${values.email.split("@")[0]}`
  });
}

Geben Sie eine E-Mail-Adresse und ein Passwort ein, und senden Sie dann das Formular ab, um Ihren neuen Benutzernamen zu sehen!

Zusammenfassung

Wow, wir haben in kurzer Zeit viel Stoff behandelt. Auch wenn dies nur die Spitze des Eisbergs ist, was die Erfüllung aller Bedürfnisse eines Formulars und die Fähigkeiten von Formik betrifft, hoffe ich, dass Ihnen dies ein neues Werkzeug an die Hand gibt, das Sie greifen können, wenn Sie das nächste Mal Formulare in einer React-Anwendung angehen.

Wenn Sie bereit sind, Formik auf die nächste Stufe zu heben, schlage ich vor, sich ihre Ressourcen anzusehen als Ausgangspunkt. Dort gibt es so viele Leckerbissen und es ist ein gutes Archiv dessen, was Formik leisten kann, sowie weitere Tutorials, die sich mit tiefergehenden Anwendungsfällen befassen.

Viel Erfolg mit Ihren Formularen!