Skip to content

はじめる

React Hook Form によるシンプルなフォームバリデーション。

インストール

一つのインストールコマンドだけで、React Hook Form を使用する準備が整います。

npm install react-hook-form

下記のコードは基本的な使用法を示します。

CodeSandbox
import React from 'react'
import { useForm } from 'react-hook-form'

export default function App() {
  const { register, handleSubmit, watch, errors } = useForm()
  const onSubmit = data => { console.log(data) }

  console.log(watch('example')) // watch input value by passing the name of it

  return (
    {/* "handleSubmit" will validate your inputs before invoking "onSubmit" */}
    <form onSubmit={handleSubmit(onSubmit)}>
    {/* register your input into the hook by invoking the "register" function */}
      <input name="example" defaultValue="test" ref={register} />
      
      {/* include validation with required or other standard HTML validation rules */}
      <input name="exampleRequired" ref={register({ required: true })} />
      {/* errors will return when field validation fails  */}
      {errors.exampleRequired && <span>This field is required</span>}
      
      <input type="submit" />
    </form>
  )
}

ビデオチュートリアル

このビデオチュートリアルでは、React Hook Form の基本的な使用法とコンセプトを説明します。

フィールドを登録する

React Hook Form の重要なコンセプトの一つは、非制御コンポーネント (Uncontrolled Components) をフックに登録(register) し、フォームフィールドの値を検証と収集できるようにすることです。

注意: 各フィールドには登録プロセスの key としてユニークな name 属性が必須です。

注意:React Native は手動登録 (manual register) する必要があります。 (例: register({ name: 'test' }, { required: true })または、Controllerを使用してコンポーネントをラップします。React Nativeセクションで詳細を読むこともできます。

import React from 'react'
import { useForm } from 'react-hook-form'

export default function App() {
  const { register, handleSubmit } = useForm()
  const onSubmit = data => console.log(data)
   
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register} />
      <select name="gender" ref={register}>
        <option value="male">male</option>
        <option value="female">female</option>
      </select>
      <input type="submit" />
    </form>
  );
}

バリデーションを適用する

React Hook Form は既存のHTML 標準のフォームバリデーション合わせることにより、フォームバリデーションを容易にします。

サポートされているバリデーションルール一覧:

  • required
  • min
  • max
  • minLength
  • maxLength
  • pattern
  • validate

register セクションで各ルールの詳細を読むことができます。

import React from 'react'
import { useForm } from 'react-hook-form'

export default function App() {
  const { register, handleSubmit } = useForm()
  const onSubmit = data => console.log(data)
   
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={register({ required: true, maxLength: 20 })} />
      <input name="lastName" ref={register({ pattern: /^[A-Za-z]+$/i })} />
      <input name="age" type="number" ref={register({ min: 18, max: 99 })} />
      <input type="submit" />
    </form>
  );
}

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

既存のフォームの処理はシンプルです。重要なステップは、既存のコンポーネントの refregister を適用することです。
CodeSandbox
import React from 'react'
import { useForm } from 'react-hook-form'

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

// you can use React.forwardRef to pass the ref too
const Select = React.forwardRef(({ label, register }, ref) => ( 
  <>
    <label>{label}</label>
    <select name={label} ref={ref}>
      <option value="20">20</option>
      <option value="30">30</option>
    </select>
  </>
))

export default function App() {
  const { register, handleSubmit } = useForm()
  const onSubmit = data => console.log(data)
   
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input label="First Name" register={register} required />
      <Select label="Age" ref={register} />
      <input type="submit" />
    </form>
  )
}

UI ライブラリを使用する

React Hook Form は、外部 UI コンポーネントライブラリとの統合が容易です。

オプション1: 最適な方法は、使用したい外部コンポーネントが register に使用できる innerRef または ref を公開しているかどうかを確認することです。 例えば、Material-UI の TextField は、 props の1つとして inputRef を受け付けます。inputRefregister を渡すだけです

<TextField inputRef={register} label="First name" name="FirstName"/>

オプション2: 例えば、 react-selectreact-datepicker などのように、 コンポーネントによっては register のための prop が公開されていないことがあります。

次に簡単な方法は、ラッパーコンポーネントである Controller を使用することです。 このコンポーネントはカスタム登録処理を行います。

import React from "react";
import { useForm, Controller } from "react-hook-form";
import Select from "react-select";
import Input from "@material-ui/core/Input";
import { Input as InputField } from "antd";

export default function App() {
  const { control, handleSubmit } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller as={Input} name="HelloWorld" control={control} defaultValue="" />
      <Controller as={InputField} name="AntdInput" control={control} defaultValue="" />
      <Controller
        as={Select}
        name="reactSelect"
        control={control}
        onChange={([selected]) => {
          // React Select return object instead of value for selection
          return { value: selected };
        }}
        defaultValue={{}}
      />

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

オプション3: 最後に useEffect フックを使用してカスタム登録を設定し、 setValue を介して値を更新できます。

CodeSandbox
import React from 'react';
import { useForm } from 'react-hook-form';
import Select from "react-select";
import Input from "@material-ui/core/Input";
import { Input as InputField } from 'antd';

export default function App() {
  const { register, handleSubmit, setValue } = useForm();
  const onSubmit = data => console.log(data);
  const [values, setReactSelectValue] = useState({ selectedOption: [] });

  const handleMultiChange = selectedOption => {
    setValue("reactSelect", selectedOption);
    setReactSelectValue({ selectedOption });
  }
  
  const handleChange = (e) => {
    setValue("AntdInput", e.target.value);
  }
  
  React.useEffect(() => {
    register({ name: "reactSelect" }); // custom register react-select 
    register({ name: "AntdInput" }); // custom register antd input
  }, [register])

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input name="HelloWorld" inputRef={register} />
      <InputField name="AntdInput" onChange={handleChange} />
      <Select
        value={values.selectedOption}
        options={options}
        onChange={handleMultiChange}
        isMulti
      />
      <input type="submit" />
    </form>
  );
}

制御された Input

React Hook Form は、非制御コンポーネントとネイティブ HTML input をサポートしますが、 React-Select AntD Material-UI などの外部の制御された UI コンポーネントライブラリと組み合わせての使用を避けることは難しいため、 ラッパーコンポーネントを作成しました。 Controller は、必要に応じてカスタム登録を自由に使用できると同時に、統合プロセスを簡素化します。

CodeSandbox
import React from "react";
import { useForm, Controller } from "react-hook-form";
import ReactSelect from "react-select";
import { TextField, Checkbox } from "@material-ui/core";

function App() {
  const methods = useForm();
  const { handleSubmit, control, reset } = methods;
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* Option 1 (preferred): pass a component to the Controller. */}
      <Controller as={TextField} name="TextField" control={control} defaultValue="" />
      
      {/* Option 2: pass a JSX element to the Controller. */}
      {/* Note that any prop passed to the element will be overriden. */}
      {/* In this case, "SomeName" will be changed to "MyCheckbox". */}
      <Controller
        as={<Checkbox name="SomeName"/>}
        name="MyCheckbox"
        value="test"
        control={control}
        defaultValue={false}
      />

      <button>Submit</button>
    </form>
  );
}

グローバルな状態に統合する

React Hook Form では、データを保存するために状態管理ライブラリを使用する必要はありませんが、簡単に統合することができます。
import React from 'react'
import { useForm } from 'react-hook-form'
import { connect } from 'react-redux'
import updateAction from './actions'

export default function App(props) {
  const { register, handleSubmit, setValue } = useForm()
  // Submit your data into Redux store
  const onSubmit = (data) => props.updateAction(data)
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input name="firstName" defaultValue={props.firstName} ref={register} />
      <Input name="lastName" defaultValue={props.lastName} ref={register} />
      <input type="submit" />
    </form>
  );
}

// Connect your component with redux
connect(({ firstName, lastName }) => ({ firstName, lastName }), updateAction)(YourForm)

エラーを処理する

React Hook Form はフォーム内のエラーを表す errors オブジェクトを提供しています。
import React from 'react'
import { useForm } from 'react-hook-form'

export default function App() {
  const { register, errors, handleSubmit } = useForm()
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input name="firstName" ref={register({ required: true })} />
      {errors.firstName && 'First name is required'}
      <Input name="lastName" ref={register({ required: true })} />
      {errors.lastName && 'Last name is required'}
      <input type="submit" />
    </form>
  );
}

React Native

非制御コンポーネントでも、同じようにパフォーマンスが向上します。 ただし、React Native と互換性のない API がいくつかあります (Web とネイティブとの API の違い)。 下記の例に示すように、手動登録 (manual register) を使用する必要があります。

Expo
import React from "react";
import { Text, View, TextInput, Button, Alert } from "react-native";
import { useForm, Controller } from "react-hook-form";

export default function App() {
  const { control, handleSubmit, errors } = useForm();
  const onSubmit = data => Alert.alert(
    "Form Data",
    JSON.stringify(data),
  );

  return (
    <View>
      <Text>First name</Text>
      <Controller
        as={TextInput}
        control={control}
        name="firstName"
        onChange={args => args[0].nativeEvent.text}
        rules={{ required: true }}
        defaultValue=""
      />
      {errors.firstName && <Text>This is required.</Text>}

      <Text>Last name</Text>
      <Controller
        as={TextInput}
        control={control}
        name="lastName"
        onChange={args => args[0].nativeEvent.text}
        defaultValue=""
      />


      <Button title="Submit" onPress={handleSubmit(onSubmit)} />
    </View>
  );
}
Expo
import React from "react"
import { Text, View, TextInput, Button, Alert } from "react-native"
import { useForm } from 'react-hook-form'

export default function App() {
  const { register, setValue, handleSubmit, errors } = useForm()
  const onSubmit = data => Alert.alert('Form Data', data)

  return (
    <View>
      <Text>First name</Text>
      <TextInput
        ref={register({ name: 'firstName'}, { required: true })}
        onChangeText={text => setValue('firstName', text, true)}
      />
      {errors.firstName && <Text>This is required.</Text>}

      <Text>Last name</Text>
      <TextInput
        ref={register({ name: 'lastName'})}
        onChangeText={text => setValue('lastName', text)}
      />

      <Button onPress={handleSubmit(onSubmit)} />
    </View>
  )
}

TypeScript

React Hook Form は Typescript を使用して構築されているため、フォームの値をサポートするための FormData 型を定義することができます。

CodeSandbox
import * as React from "react";
import { useForm } from "react-hook-form";

type FormData = {
  firstName: string;
  lastName: string;
};

export default function App() {
  const { register, setValue, handleSubmit, errors } = useForm<FormData>();
  const onSubmit = handleSubmit(({ firstName, lastName }) => {
    console.log(firstName, lastName);
  }); // firstName and lastName will have correct type

  return (
    <form onSubmit={onSubmit}>
      <label>First Name</label>
      <input name="firstName" ref={register} />
      <label>Last Name</label>
      <input name="lastName" ref={register} />
      <button
        type="button"
        onClick={() => {
          setValue("lastName", "luo"); // ✅
          setValue("firstName", true); // ❌: true is not string
          errors.bill; // ❌: property bill does not exist
        }}
      >
        SetValue
      </button>
    </form>
  );
}

もっと知りたいですか?

React Hook Form のドキュメントを見て、API に関する全ての情報を確認してください。