Chakra UIとReact Hook FormとYupを使ってフォームを作成する

こんにちは。イチマルイチデザインのフロントエンドエンジニアの高橋です。

今回は、Chakra UIとReact Hook FormとYupを使用して、ユーザビリティ高めのフォームを比較的簡単に作成する方法を共有します。

使用するライブラリ

実装方法

こちらが実際のコードの全体像となります。

import {
 Button,
 Container,
 FormControl,
 FormErrorMessage,
 FormLabel,
 Input,
 Textarea,
 VStack,
} from '@chakra-ui/react';
import { yupResolver } from '@hookform/resolvers/yup';
import React from 'react';
import { useForm } from 'react-hook-form';
import { InferType, object, SchemaOf, string } from 'yup';

type Schema = {
 company: string;
 email: string;
 message: string;
};

const schema: SchemaOf<Schema> = object({
 company: string().required('会社名は必須です。'),
 email: string().required('メールアドレスは必須です。'),
 message: string().required('メッセージは必須です。'),
});

const Form: React.VFC = () => {
 const {
   register,
   handleSubmit,
   formState: { errors },
 } = useForm<InferType<typeof schema>>({
   resolver: yupResolver(schema),
 });

 const handleSubmitForm = handleSubmit(({ company, email, message }) => {
   console.log(company, email, message);
 });

 return (
   <Container>
     <form onSubmit={handleSubmitForm}>
       <VStack>
         <FormControl isInvalid={!!errors['company']}>
           <FormLabel htmlFor={'company'}>会社名</FormLabel>
           <Input id={'company'} {...register('company')} />
           <FormErrorMessage>
             {errors['company'] && errors['company']?.message}
           </FormErrorMessage>
         </FormControl>

         <FormControl isInvalid={!!errors['email']}>
           <FormLabel htmlFor={'email'}>メールアドレス</FormLabel>
           <Input id={'email'} {...register('email')} />
           <FormErrorMessage>
             {errors['email'] && errors['email']?.message}
           </FormErrorMessage>
         </FormControl>

         <FormControl isInvalid={!!errors['message']}>
           <FormLabel htmlFor={'message'}>メッセージ</FormLabel>
           <Textarea id={'message'} rows={10} {...register('message')} />
           <FormErrorMessage>
             {errors['message'] && errors['message']?.message}
           </FormErrorMessage>
         </FormControl>

         <Button type={'submit'}>送信</Button>
       </VStack>
     </form>
   </Container>
 );
};

export default Form;

以下、解説です。

バリデーションのスキーマを定義

type Schema = {
  company: string;
  email: string;
  message: string;
};

フォームの項目をそれぞれ定義した Schema 型を作成します。

const schema: SchemaOf<Schema> = object({
  company: string().required('会社名は必須です。'),
  email: string().required('メールアドレスは必須です。'),
  message: string().required('メッセージは必須です。'),
});

実際にバリデーションとしての設定となります。
Yupの SchemaOf 型を型アノテーションとして指定した schema を定義しています。

SchemaOf 型のジェネリクスにオリジナルの型として定義した Schema を指定することで、意図しないプロパティを追加してもTypeScriptが怒ってくれます。

Yupのバリデーションの設定は、さまざまなバリデーションをメソッドチェーンで定義できますが、今回は必須項目として登録する required のみ追加しています。

required の引数に文字列を渡すことによって、入力がなかった場合に引数に渡した文字列を出力できるようになります。

React Hook FormのuseFormの設定

const {
  register,
  handleSubmit,
  formState: { errors },
} = useForm<InferType<typeof schema>>({
  resolver: yupResolver(schema),
});

Yupの InferType 型のジェネリクスに対して、先ほど定義した変数 schema から typeof演算子で抽出した型定義を定義しています。

さらにこの型定義を useForm のジェネリクスに渡すことによって、様々な恩恵を受けることが可能になります。

Yupで定義したスキーマをReact Hook Formが提供している yupResolver を介して、resolver プロパティに登録することで、React Hook FormにYupのバリデーションの設定が使用できるようになります。

Chakra UIのコンポーネントにReact Hook Formの設定を連携させる

<form onSubmit={handleSubmitForm}>
  <VStack>
    <FormControl isInvalid={!!errors['company']}>
      <FormLabel htmlFor={'company'}>会社名</FormLabel>
      <Input id={'company'} {...register('company')} />
      <FormErrorMessage>
        {errors['company'] && errors['company']?.message}
      </FormErrorMessage>
    </FormControl>

    <FormControl isInvalid={!!errors['email']}>
      <FormLabel htmlFor={'email'}>メールアドレス</FormLabel>
      <Input id={'email'} {...register('email')} />
      <FormErrorMessage>
        {errors['email'] && errors['email']?.message}
      </FormErrorMessage>
    </FormControl>

    <FormControl isInvalid={!!errors['message']}>
      <FormLabel htmlFor={'message'}>メッセージ</FormLabel>
      <Textarea id={'message'} rows={10} {...register('message')} />
      <FormErrorMessage>
        {errors['message'] && errors['message']?.message}
      </FormErrorMessage>
    </FormControl>

    <Button type={'submit'}>送信</Button>
  </VStack>
</form>

Chakra UIには FormControl というコンポーネントが存在し、このコンポーネントは子コンポーネントに様々な状態のコンテキストを渡すラッパーとなっています。

FormControl の isInvalid の値が true の場合は、子コンポーネントの FormErrorMessage が表示され、子コンポーネントの Input や Textarea コンポーネントのスタイルがエラー時の状態に変化します。

FormControl の isInvalid に errors の各項目を指定することで、ある項目がバリデーションのチェックに引っかかった場合、その項目のエラーメッセージの出力とスタイルの変更を全てChakra UI側でやってくれるようになります。

終わりに

株式会社イチマルイチデザインでは、私たちと一緒にReact・Next.js、TypeScriptなどの技術を使ったWEBアプリケーションやWEBサイトの開発をするフロントエンドエンジニアの仲間を募集しています。

デザイナーやバックエンドエンジニアの方も募集をしておりますので、興味がある方はご連絡お待ちしております。

▼採用サイトはこちら
https://101de-sign.com/recruit