nomurabbitのブログ

nomurabbitのブログはITを中心にした技術ブログです。

nomurabbitのブログ

【React】React Hook Formの "はじめる" を始めてみた Part4【Typescript】

この記事はReact Hook Formの公式サイトにあるはじめるの項目を元に構成されています。ReactとTypescriptのサンプルコードを使って解説しています。

こんにちは!らびです。今回はReact Hook Formに関する記事です。

公式サイトのチュートリアルを使って一緒に勉強していきましょう。

既存のフォームに適用する

今回のサンプルコードは既存のフォームに適用するということで、再利用を想定して関数化された入力フォームでの使い方です。ということで、React Hook FormのサンプルコードをReactのプロジェクトにうつしたものがこちらです。

import React from 'react';
import { Path, useForm, UseFormRegister, SubmitHandler } from "react-hook-form";
import {List, ListItem} from '@mui/material';
import ResponsiveDrawer from '../components/layout/ResponsiveDrawer';

interface IFormValues {
  "First Name": string;
  Age: number;
}

type InputProps = {
  label: Path<IFormValues>;
  register: UseFormRegister<IFormValues>;
  required: boolean;
};

// The following component is an example of your existing Input Component
const Input = ({ label, register, required }: InputProps) => (
  <>
    <label>{label}</label>
    <input {...register(label, { required })} />
  </>
);

// you can use React.forwardRef to pass the ref too
const Select = React.forwardRef<
  HTMLSelectElement,
  { label: string } & ReturnType<UseFormRegister<IFormValues>>
>(({ onChange, onBlur, name, label }, ref) => (
  <>
    <label>{label}</label>
    <select name={name} ref={ref} onChange={onChange} onBlur={onBlur}>
      <option value="20">20</option>
      <option value="30">30</option>
    </select>
  </>
));

const Home4 = () => {
  const { register, handleSubmit } = useForm<IFormValues>();
  const onSubmit: SubmitHandler<IFormValues> = (data) => {
    alert(JSON.stringify(data));
  };
  
  return (
    <ResponsiveDrawer>
      <div>
        <form onSubmit={handleSubmit(onSubmit)}>
          <List>
            <ListItem>
              <label>First Name</label>
              <Input label="First Name" register={register} required />
            </ListItem><ListItem>
              <label>Age</label>
              <Select label="Age" {...register("Age")} />
            </ListItem><ListItem>
              <input type="submit" />
            </ListItem>
          </List>
        </form>
      </div>
    </ResponsiveDrawer>
  );
}
  
export default Home4;

InputとSelectという2つの関数化された入力フォームが定義されてるジョ。実行結果はこうなるジョ。

f:id:nomurabbit:20220110100941p:plain

import

まずはimportです。今回はuseFormSubmitHandlerの他にPathUseFormRegisterをインポートしています。

import { Path, useForm, UseFormRegister, SubmitHandler } from "react-hook-form";


関数化された入力フォームで入力コンポーネントの登録をするのにPathUseFormRegisterが必要なんですね。

const { register, handleSubmit } = useForm<IFormValues>();


useFormのジェネリクスパラメータとしてIFormValuesを渡しているジョ。

register

次は入力コンポーネントの登録です。今回はInputSelectそれぞれの関数化された入力フォームについて見ていきましょう。

Inputの定義を見るとInputProps型の引数を取っていて、そこからlabel、register、requiredを取り出しています。

type InputProps = {
  label: Path<IFormValues>;
  register: UseFormRegister<IFormValues>;
  required: boolean;
};

const Input = ({ label, register, required }: InputProps) => (
  <>
    <label>{label}</label>
    <input {...register(label, { required })} />
  </>
);

…省略
<Input label="First Name" register={register} required />

関数の呼び出しを見ると、label、register、requiredをそれぞれ渡しています。

Selectの定義はforwardRefを使っています。forwardRefのジェネリクスパラメータはHTMLSelectElement{ label: string } & ReturnType>です。第二パラメータの{ label: string } & ReturnType>は、labelが指定されている前提でUseFormRegisterの戻り値の型が指定されている。と読めるので、定義内の無名関数にはUseFormRegisterの戻り値であるUseFormRegisterReturnが渡されます。

ちょっと複雑なので、十分に時間をかけて整理するといいジョ。

UseFormRegisterReturnの定義を見ると、onChange, onBlur, nameの定義があるので、UseFormRegisterReturn型で引数が渡されているのが確認できますね。

const Select = React.forwardRef<
  HTMLSelectElement,
  { label: string } & ReturnType<UseFormRegister<IFormValues>>
>(({ onChange, onBlur, name, label }, ref) => (
  <>
    <label>{label}</label>
    <select name={name} ref={ref} onChange={onChange} onBlur={onBlur}>
      <option value="20">20</option>
      <option value="30">30</option>
    </select>
  </>
));

…省略
<Select label="Age" {...register("Age")} />


UseFormRegisterReturnの定義

export declare type UseFormRegisterReturn = {
    onChange: ChangeHandler;
    onBlur: ChangeHandler;
    ref: RefCallBack;
    name: InternalFieldName;
    min?: string | number;
    max?: string | number;
    maxLength?: number;
    minLength?: number;
    pattern?: string;
    required?: boolean;
    disabled?: boolean;
};

まとめ

というわけで、今回もReact Hook Formはじめるについて勉強してきましたが、いかがでしたでしょうか?

再利用を想定して入力フォームを関数化してもReact Hook Formが利用できることが確認できました。

次回もぜひご覧ください。では!

参考

React Hook Form
react-hook-form.com