Bezbolestné formy s React Hook Form

V tomto tutoriálu se budeme zabývat tím, jak pracovat s formuláři a jak provádět validaci dat v Reactu. Pomocí knihovny nazvané reagovat-hook-form.

Pokud si nejste jisti, jak používat formuláře s Reactem, podívejte se na tyto 2 základní vzory formulářů React.

react-hook-form je knihovna, která usnadňuje práci s daty ve formulářích. Ověření dat je obzvláště snadné při použití react-hook-form .

Přehled:

  • Se základním ověřením
  • Ověření pro vnořená pole
  • Schémata ověření s Ano
  • Vlastní vstupní pole
  • Ověření pomocí Material-UI

Pojďme se do toho hned pustit! 💪

Se základním ověřením

Začněme tím, jak může vypadat základní ověření. 👇

import React from 'react';

import useForm from 'react-hook-form';

const LoginFormWithValidation = () => {
  const { 
    handleSubmit, // Submit handler wrapper
     register,  // Register form fields
     errors  // Errors object including error messages
     } = useForm();

  const onSubmit = values => {
    console.log(values); // email & password input's values in an object.
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="email">Email:</label>
      <input
        name="email"
        type="email"
        aria-describedby="emailError"
        ref={register({
          required: 'Email Message Required Message', // Error message when field is left empty.
          pattern: { // Validation pattern
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
            message: 'invalid email address' // Error message when validation fails.
          }
        })}
      />
      {errors.email ? (
        //
        <span id="emailError">{errors.email.message}</span>
      ) : (
        ''
      )}

      <label htmlFor="password">Password:</label>
      <input
        name="password"
        type="password"
        ref={register({
          validate: value => value !== 'test123' || 'Too common password, you can do better!' // Validation error message
        })}
        aria-describedby="passwordError"
      />
      {errors.password ? (
        <span id="passwordError">{errors.password.message}</span>
      ) : (
        ''
      )}

      <input type="submit" value="Submit" />
    </form>
  );
};

export default LoginFormWithValidation;

Podívejme se dále, jak můžeme sdílet stav a pravidla ověření napříč vnořenými prvky formuláře.

Ověření pro vnořená pole

react-hook-form využívá React Context. Umožňuje vám poskytnout kontext formuláře pomocí FormContext . A přečtěte si kontext pomocí háčku s názvem useFormContext .

import React from 'react';

import useForm, { FormContext } from 'react-hook-form';
import EmailInput from './EmailInput';

const LoginFormWithNestedInput = () => {
  const methods = useForm();

  const onSubmit = values => {
    console.log(values);
  };

  return (
    // Initialise context with all form hook methods
    <FormContext {...methods}>
      <form onSubmit={methods.handleSubmit(onSubmit)}>
        {/* EmailInput is a nested input field. */} 
        <EmailInput name="email"></EmailInput>
        <label htmlFor="password">Password:</label>
        <input
          name="password"
          type="password"
          ref={methods.register({
            validate: value => value !== 'test123' || 'You can do better'
          })}
          aria-describedby="passwordError"
        />
        {methods.errors.password ? (
          <span id="passwordError">{methods.errors.password.message}</span>
        ) : (
          ''
        )}

        <input type="submit" value="Submit" />
      </form>
    </FormContext>
  );
};

export default LoginFormWithNestedInput;

Zatím je vše v pořádku, co říkáte na EmailInput komponent, divíte se? 🧐

Tady je, jak to vypadá...

import React, { Fragment } from 'react';
import { useFormContext } from 'react-hook-form';

const EmailInput = props => {
  const { register, errors } = useFormContext(); // Regular form methods such as register is available from form context.
  return (
    <Fragment>
      <label htmlFor={props.name}>Email:</label>
      <input
        name={props.name}
        type="email"
        aria-describedby={`${props.name}-emailError`}
        ref={register({
          required: 'Required',
          pattern: {
            // Allows only nordschool.com emails
            value: /^[A-Z0-9._%+-][email protected]/i,
            message: 'Invalid email address - Only Nordschool domain is allowed'
          }
        })}
      />
      {errors[props.name] ? (
        <span id={`${props.name}-emailError`}>
          {errors[props.name].message}
        </span>
      ) : (
        ''
      )}
    </Fragment>
  );
};

export default EmailInput;

Běžným případem použití pro ověřování formulářů je použití schémat ověření.

Schémata ověření

react-hook-form umožňuje deklarovat ověřovací schémata. Použití jiné knihovny ověření s názvem yup můžeme definovat ověřovací pravidla.

Ano je validátor schématu objektů JS a analyzátor objektů. Rozhraní API je podobné jako Joi, ale je menší a výkonnější, takže je vhodné pro klientské aplikace.

Pomocí tohoto nastavení může vypadat jednoduchý přihlašovací formulář 🙌....

import React from 'react';

import useForm, { FormContext } from 'react-hook-form';
import EmailInput from './EmailInput';
import AddressInputs, { AddressSchema } from './AddressInputs';

import { string as yupstring, object as yupobject } from 'yup';

const SignupFormSchema = yupobject().shape({
  email: yupstring()
    .required('Email is unfortunately required')
    .email('Please add a real email'),
  name: yupstring().required('Name is important, what should we call you?'),
  ...AddressSchema // Custom schema imported from address inputs.
});

const SignupForm = () => {
  const methods = useForm({ validationSchema: SignupFormSchema });

  const onSubmit = values => {
    console.log(values);
  };

  return (
    <FormContext {...methods}>
      <form onSubmit={methods.handleSubmit(onSubmit)}>
        <label htmlFor="name"> Name:</label>
        <input
          name="name"
          type="text"
          aria-describedby="nameError"
          ref={methods.register()}
        />
        {methods.errors.name ? (
          <span id="nameError">{methods.errors.name.message}</span>
        ) : (
          ''
        )}
        <EmailInput name="email"></EmailInput>
        <AddressInputs name="email"></AddressInputs>
        <input type="submit" value="Search" />
      </form>
    </FormContext>
  );
};

export default SignupForm;

A AddressInputs s jejich vlastním schématem...

import React, { Fragment } from 'react';
import { useFormContext } from 'react-hook-form';

import { string as yupstring } from 'yup';

export const AddressSchema = {
  streetAddress: yupstring().required('Street address is required!'),
  postalCode: yupstring()
    .length(4)
    .required('required!'),
  city: yupstring().required('City is required!')
};

const AddressInputs = props => {
  const { register, errors } = useFormContext();
  return (
    <Fragment>
      <label htmlFor="stressAddress">Street Address:</label>
      <input
        name="streetAddress"
        type="text"
        aria-describedby="streetAddressError"
        ref={register()}
      />
      {errors.streetAddress ? (
        <span id="streetAddressError">{errors.streetAddress.message}</span>
      ) : (
        ''
      )}
      <label htmlFor="postalCode">Postal Code:</label>
      <input
        name="postalCode"
        type="text"
        aria-describedby="postalCodeError"
        ref={register()}
      />
      {errors.postalCode ? (
        <span id="postalCodeError">{errors.postalCode.message}</span>
      ) : (
        ''
      )}

      <label htmlFor="city">City:</label>
      <input
        name="city"
        type="text"
        aria-describedby="cityError"
        ref={register()}
      />
      {errors.city ? <span id="cityError">{errors.city.message}</span> : ''}
    </Fragment>
  );
};

export default AddressInputs;

Pokud jste si všimli, opakujeme vzor vstupního pole všude! Pojďme zapouzdřit prvky vstupního pole do vlastní komponenty. 😎

Vlastní vstupní pole

import React, { Fragment } from 'react';

const InputField = props => {
  return (
    <Fragment>
      <label htmlFor={props.name}>{props.label}</label>
      <input
        name={props.name}
        type={props.type || 'text'}
        aria-describedby={`${props.name}Error`}
        ref={props.registerFn}
      />
      {props.error ? (
        <span id={`${props.name}Error`}>{props.error.message}</span>
      ) : (
        ''
      )}
    </Fragment>
  );
};

export default InputField;

Nyní naše AddressInputs dalo by se to předělat, aby to vypadalo víc takhle...

import React, { Fragment } from 'react';
import { useFormContext } from 'react-hook-form';
import InputField from './InputField';
import { string as yupstring } from 'yup';

export const AddressSchema = {
  streetAddress: yupstring().required('Street address is required!'),
  postalCode: yupstring()
    .length(4)
    .required('required!'),
  city: yupstring().required('City is required!')
};

const AddressInputs = props => {
  const { register, errors } = useFormContext();
  return (
    <Fragment>
      <InputField
        label="Street Address:"
        name="stressAddress"
        error={errors.streetAddress}
        registerFn={register()}
      ></InputField>
      <InputField
        label="Postal Code:"
        name="postalCode"
        error={errors.postalCode}
        registerFn={register()}
      ></InputField>
      <InputField
        label="City:"
        name="stressAddress"
        error={errors.city}
        registerFn={register()}
      ></InputField>
    </Fragment>
  );
};

export default AddressInputs;

Mnohem hezčí! 👌

Dobře, zatím dobře, teď už víte dost na to, abyste se dostali pěkně daleko!

A co použití knihovny komponent?

Pojďme se rychle podívat, jak můžeme použít react-hook-form a material-ui dohromady.

Ověření pomocí Material-UI

import React from 'react';
import useForm from 'react-hook-form';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';

import { string as yupstring, object as yupobject } from 'yup';

const ContactFormSchema = yupobject().shape({
  email: yupstring()
    .required('Email is required')
    .email('Please enter a valid email'),
  message: yupstring().required('Please tell us how we can help you'),
  name: yupstring().required('Name is important, what should we call?')
});

const ContactForm = () => {
  const { register, errors, handleSubmit } = useForm({
    validationSchema: ContactFormSchema
  });

  const onSubmit = values => console.log(values);
  return (
    <form autoComplete="off" onSubmit={handleSubmit(onSubmit)} noValidate>
      <TextField
        id="name"
        label="Name"
        name="name"
        inputRef={register}
        placeholder="Joe"
        margin="normal"
        variant="outlined"
        error={errors.name ? true : false}
        helperText={errors.name ? errors.name.message : ''}
      />
      <TextField
        id="email"
        label="Email"
        name="email"
        inputRef={register}
        placeholder="[email protected]"
        margin="normal"
        variant="outlined"
        error={errors.email ? true : false}
        helperText={errors.email ? errors.email.message : ''}
      />
      <TextField
        required
        id="message"
        multiline
        rows="4"
        name="message"
        inputRef={register}
        label="How can we help you today?"
        placeholder="Some pizza please!"
        margin="normal"
        variant="outlined"
        error={errors.message ? true : false}
        helperText={errors.message ? errors.message.message : ''}
      />
      <Button variant="contained" type="submit">
        Submit
      </Button>
    </form>
  );
};

export default ContactForm;

To je vše, nyní je vaše základní školení pro ověřování formulářů dokončeno! 🎖️

Podpora

Líbil se vám článek? Sdílejte souhrnné vlákno na twitteru.

Pondělní bulletin Better Code

Mohl by se vám také líbit můj newsletter. Cílem je podělit se každé pondělí o 3 tipy pro webové vývojáře.

Mým cílem je zlepšit své schopnosti psaní a sdílet znalosti co nejvíce. Zatím se přihlásilo několik stovek vývojářů a zdá se, že se jim to líbí.

Chcete-li získat představu o tom, jaký druh věcí sdílím, podívejte se na předchozí vydání zpravodaje a přihlaste se.