0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactを学ぶ React Hook Form

Posted at

React Hook Form とは

React Hook Form は、React におけるフォームの状態管理とバリデーションを効率的に行うためのライブラリ。フォーム入力を useState で1つずつ管理する従来の方法では、入力のたびに再レンダーが発生し、フォームが大きくなるほどパフォーマンスが低下する問題があった。

import { useState } from "react";

function App() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [error, setError] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();

    if (!name) {
      setError("名前は必須です");
      return;
    }

    if (!email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
      setError("メールアドレスの形式が正しくありません");
      return;
    }

    setError("");
    console.log({ name, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        名前:
        <input
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </label>

      <label>
        メールアドレス:
        <input
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
      </label>

      {error && <p style={{ color: "red" }}>{error}</p>}

      <button type="submit">送信</button>
    </form>
  );
}

export default App;

React Hook Form は内部的に ref を使って値を管理し、再レンダーを最小限に抑えることでこの問題を解決している。
さらに、バリデーションを簡潔に記述できる点も特徴である。各フィールドを register で登録し、バリデーションルールを指定するだけで、未入力や形式エラーなどを自動的に検出できる。yup や zod といったスキーマバリデーションライブラリとの連携にも対応しており、Material UI などの UI フレームワークとも統合しやすい。

import { useForm } from "react-hook-form";

function App() {
  const { register, handleSubmit, formState: { errors } } = useForm();

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>
        名前:
        <input {...register("name", { required: "名前は必須です" })} />
      </label>
      {errors.name && <p style={{ color: "red" }}>{errors.name.message}</p>}

      <label>
        メールアドレス:
        <input
          {...register("email", {
            required: "メールアドレスは必須です",
            pattern: {
              value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
              message: "メールアドレスの形式が正しくありません",
            },
          })}
        />
      </label>
      {errors.email && <p style={{ color: "red" }}>{errors.email.message}</p>}

      <button type="submit">送信</button>
    </form>
  );
}

export default App;

つまり、React Hook Form はフォームに関する複雑な状態管理やバリデーション処理をシンプルにし、React アプリケーションのパフォーマンスと可読性を両立させるための仕組みである

フィールドの登録

React Hook Form の基本となる考え方のひとつは、入力コンポーネントをフックに「登録(register)」すること。登録されたフィールドの値は、フォームのバリデーション(入力チェック)や送信処理で利用できるようになる。

import ReactDOM from "react-dom"
import { useForm, SubmitHandler } from "react-hook-form"

enum GenderEnum {
  female = "female",
  male = "male",
  other = "other",
}

interface IFormInput {
  firstName: string
  gender: GenderEnum
}

export default function App() {
  const { register, handleSubmit } = useForm<IFormInput>()
  const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>First Name</label>
      <input {...register("firstName")} />
      <label>Gender Selection</label>
      <select {...register("gender")}>
        <option value="female">female</option>
        <option value="male">male</option>
        <option value="other">other</option>
      </select>
      <input type="submit" />
    </form>
  )
}

この記述は、実際には以下のように展開されているのと同じ意味を持つ。

<input
  name="firstName"
  ref={...}  
  onChange={...}
  onBlur={...}
/>

つまり、registerref やイベントハンドラを自動で設定し、入力の値・フォーカス・バリデーションなどをReact Hook Form の内部状態に同期させる仕組み。

フィールドを登録するには、必ず name 属性 を指定する必要がある。

バリデーションの適用

React Hook Form は、既存の HTML 標準のフォームバリデーション仕様に沿って動作するため、
シンプルな記述で入力チェック(バリデーション)を実装できる。

ルール名 説明
required 入力必須かどうかを指定する。
min 数値入力における最小値を指定する。
max 数値入力における最大値を指定する。
minLength 文字列の最小文字数を指定する。
maxLength 文字列の最大文字数を指定する。
pattern 正規表現パターンで入力内容をチェックする。
validate カスタム関数で自由にバリデーションを定義する。
import { useForm, SubmitHandler } from "react-hook-form"

interface IFormInput {
  firstName: string
  lastName: string
  age: number
}

export default function App() {
  const { register, handleSubmit } = useForm<IFormInput>()
  const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName", { required: true, maxLength: 20 })} />
      <input {...register("lastName", { pattern: /^[A-Za-z]+$/i })} />
      <input type="number" {...register("age", { min: 18, max: 99 })} />
      <input type="submit" />
    </form>
  )
}

既存フォームとの統合

既存のフォームコンポーネントを React Hook Form に組み込むのは基本的に簡単。
重要なのは、コンポーネントの ref を登録(register)し、必要なプロパティを入力要素に渡すこと。

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


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 App = () => {
  const { register, handleSubmit } = useForm<IFormValues>()


  const onSubmit: SubmitHandler<IFormValues> = (data) => {
    alert(JSON.stringify(data))
  }


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input label="First Name" register={register} required />
      <Select label="Age" {...register("Age")} />
      <input type="submit" />
    </form>
  )
}

UIライブラリとの統合

React Hook Form は、外部の UI コンポーネントライブラリとの統合を簡単にしている。コンポーネントが入力要素の ref を直接提供していない場合は、Controller コンポーネントを使うことで登録処理を自動的に行うことができる。

import Select from "react-select"
import { useForm, Controller, SubmitHandler } from "react-hook-form"
import { Input } from "@material-ui/core"


interface IFormInput {
  firstName: string
  lastName: string
  iceCreamType: { label: string; value: string }
}


const App = () => {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      firstName: "",
      lastName: "",
      iceCreamType: {},
    },
  })


  const onSubmit: SubmitHandler<IFormInput> = (data) => {
    console.log(data)
  }


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="firstName"
        control={control}
        render={({ field }) => <Input {...field} />}
      />
      <Controller
        name="iceCreamType"
        control={control}
        render={({ field }) => (
          <Select
            {...field}
            options={[
              { value: "chocolate", label: "Chocolate" },
              { value: "strawberry", label: "Strawberry" },
              { value: "vanilla", label: "Vanilla" },
            ]}
          />
        )}
      />
      <input type="submit" />
    </form>
  )
}

制御された入力の統合

React Hook Form は基本的に制御されていないコンポーネントやネイティブ HTML 入力を推奨している。しかし、shadcn/ui、React-Select、AntD、MUI などの外部のコントロールされたコンポーネントを使わざるを得ない場合もある。そのような場合に簡単に統合できるよう、Controller コンポーネントが用意されており、登録処理を自動で行いつつ、カスタムの register を使う自由も提供している。

コンポーネントを使った場合
import { useForm, Controller, SubmitHandler } from "react-hook-form"
import { TextField, Checkbox } from "@material-ui/core"


interface IFormInputs {
  TextField: string
  MyCheckbox: boolean
}


function App() {
  const { handleSubmit, control, reset } = useForm<IFormInputs>({
    defaultValues: {
      MyCheckbox: false,
    },
  })
  const onSubmit: SubmitHandler<IFormInputs> = (data) => console.log(data)


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="MyCheckbox"
        control={control}
        rules={{ required: true }}
        render={({ field }) => <Checkbox {...field} />}
      />
      <input type="submit" />
    </form>
  )
}
フックAPIを使った場合
import * as React from "react"
import { useForm, useController, UseControllerProps } from "react-hook-form"


type FormValues = {
  FirstName: string
}


function Input(props: UseControllerProps<FormValues>) {
  const { field, fieldState } = useController(props)


  return (
    <div>
      <input {...field} placeholder={props.name} />
      <p>{fieldState.isTouched && "Touched"}</p>
      <p>{fieldState.isDirty && "Dirty"}</p>
      <p>{fieldState.invalid ? "invalid" : "valid"}</p>
    </div>
  )
}


export default function App() {
  const { handleSubmit, control } = useForm<FormValues>({
    defaultValues: {
      FirstName: "",
    },
    mode: "onChange",
  })
  const onSubmit = (data: FormValues) => console.log(data)


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input control={control} name="FirstName" rules={{ required: true }} />
      <input type="submit" />
    </form>
  )
}

グローバルステートとの統合

React Hook Form は、必ずしも状態管理ライブラリに依存する必要はないが、必要に応じて簡単に統合することもできる。

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({
    defaultValues: {
      firstName: "",
      lastName: "",
    },
  })
  // Submit your data into Redux store
  const onSubmit = (data) => props.updateAction(data)


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} />
      <input {...register("lastName")} />
      <input type="submit" />
    </form>
  )
}


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

エラーの処理

React Hook Form では、フォーム内のエラーを確認できる errors オブジェクトが用意されている。errors の型は設定したバリデーションルールに応じて決まる。以下の例では、必須入力(required)のバリデーションルールを使った場合を示している。

import { useForm } from "react-hook-form"


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


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("firstName", { required: true })}
        aria-invalid={errors.firstName ? "true" : "false"}
      />
      {errors.firstName?.type === "required" && (
        <p role="alert">First name is required</p>
      )}


      <input
        {...register("mail", { required: "Email Address is required" })}
        aria-invalid={errors.mail ? "true" : "false"}
      />
      {errors.mail && <p role="alert">{errors.mail.message}</p>}


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

外部サービスとの統合

React Hook Form を外部サービスと連携させるには、ライブラリに用意されている送信処理機能を利用できる。<Form /> コンポーネントを使うと、フォームのデータを簡単に API エンドポイントやその他のサービスに送信できる。

import { Form } from "react-hook-form"

function App() {
  const { register, control } = useForm()

  return (
    <Form
      action="/api/save" // Send post request with the FormData
      // encType={'application/json'} you can also switch to json object
      onSuccess={() => {
        alert("Your application is updated.")
      }}
      onError={() => {
        alert("Submission has failed.")
      }}
      control={control}
    >
      <input {...register("firstName", { required: true })} />
      <input {...register("lastName", { required: true })} />
      <button>Submit</button>
    </Form>
  )
}

スキーマバリデーション

React Hook Form では、Yup、Zod、Superstruct、Joi などのスキーマベースのバリデーションもサポートしている。作成したスキーマを useForm にオプションとして渡すと、入力データがスキーマに沿って検証され、エラー情報か有効な結果が返される。

import { useForm } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import * as yup from "yup"


const schema = yup
  .object({
    firstName: yup.string().required(),
    age: yup.number().positive().integer().required(),
  })
  .required()


export default function App() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(schema),
  })
  const onSubmit = (data) => console.log(data)


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} />
      <p>{errors.firstName?.message}</p>


      <input {...register("age")} />
      <p>{errors.age?.message}</p>


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

useForm

useForm はフォームを簡単に管理するためのカスタムフックで、オプションとして 1 つのオブジェクトを引数に取ることができる。以下の例では、useForm のすべてのプロパティとそれぞれのデフォルト値を示している。

オプション名 説明
mode 送信前のバリデーション戦略を指定する。
reValidateMode 送信後のバリデーション戦略を指定する。
defaultValues フォームの初期値。キャッシュされる。
values リアクティブにフォームの値を更新する。
errors サーバーから返されたエラーでフォームを更新。 ⚠ 重要: 無限レンダリングを避けるため、errors オブジェクトの参照は安定させる。
resetOptions フォームの状態をリセットする際のオプション。
criteriaMode バリデーションエラーをすべて表示するか、1 つだけ表示するかを指定する。
shouldFocusError バリデーションエラー時の自動フォーカスを有効/無効にする。
delayError エラー表示を即時ではなく遅延させる。
shouldUseNativeValidation ブラウザ組み込みのフォーム制約 API を使用する。
shouldUnregister コンポーネントのアンマウント後に入力を自動的に登録解除するかを指定する。
disabled フォーム全体とすべての関連入力を無効化する。

register

register: (name: string, options?: RegisterOptions) => ({ ref, name, onChange, onBlur })

register を使うと、入力欄やセレクトボックスを React Hook Form に登録し、バリデーションルールを適用できる。バリデーションは HTML 標準に基づくものに加え、独自のカスタム検証も設定可能。

Props

名前 説明
name string 入力要素の名前
options RegisterOptions 入力の振る舞い(バリデーションなど)

Return

名前 説明
ref React.ref フックフォームと入力を接続するための React 要素の ref
name string 登録される入力要素の名前
onChange ChangeHandler 入力の変更イベントに対応する onChange プロパティ
onBlur ChangeHandler 入力のフォーカスアウトイベントに対応する onBlur プロパティ

unregister

unregister: (name: string | string[], options) => void

unregister を使うと、単一の入力または複数の入力をフォームから登録解除できる。また、オプションの第2引数を指定することで、入力を登録解除した後も状態を保持することができる。

<input {...register('yourDetails.firstName')} />
<input {...register('yourDetails.lastName')} />

Props

入力名
string unregister("yourDetails") {}
string unregister("yourDetails.firstName") { lastName: '' }
string[] unregister(["yourDetails.lastName"]) ''

入力フィールドを削除したり、条件によって一時的にフォームから除外したいときに使う。そのフィールドの値は getValues()watch() にも現れなくなる

formState

formState: Object

formState は、フォーム全体の状態に関する情報がまとまっている。ユーザーがフォームでどんな操作をしているかを把握し続けるために役立つ。

Return

名前 説明
isDirty boolean どれか1つでも入力値が初期値から変更されたら true。フォーム全体の「変更あり」。defaultValues を useForm で必ず設定する必要がある。
dirtyFields object ユーザーが変更した項目だけを記録したオブジェクト。フィールド単位の「変更あり」。isDirty とは目的が違う。
touchedFields object ユーザーが触った(フォーカスした/変更しようとした)項目の一覧。
defaultValues object useForm で指定した初期値、または reset で更新された初期値。状態比較の基準になる。
isSubmitted boolean フォームが一度でも送信されたら true(reset まで保持される)。
isSubmitSuccessful boolean エラーなく送信が成功したら true。
isSubmitting boolean 送信中なら true。非同期処理中も true。
isLoading boolean defaultValues を async 関数で取得している時にロード中を表す。
submitCount number フォームを送信した回数。
isValid boolean フォーム全体にエラーがなければ true。setError の有無ではなく、最終的なバリデーション結果で決まる。
isValidating boolean バリデーション中なら true。
validatingFields object 非同期バリデーション中のフィールド一覧。
errors object エラー内容の一覧。フィールドごとにエラー情報が入る。
disabled boolean useForm の disabled オプションによってフォーム全体が無効化されている場合 true。
isReady boolean formState の内部購読(subscription)が完了して、子コンポーネントでも安全に useForm のメソッドを使える状態。

watch

watch: UseFormWatch

watch は、指定した入力項目を監視し、その値を返す。入力値を画面に表示したり、条件に応じて描画内容を切り替えたい場合に便利。

Overloads

watch(name: string, defaultValue?: unknown): unknown
watch(names: string[], defaultValue?: {[key:string]: unknown}): unknown[]
watch(): {[key:string]: unknown}

単一フィールドを監視する watch(name: string, defaultValue?: unknown): unknown

レンダー関数の外で使われている単一フィールドを監視し、その変化を購読する。

Params

名前 説明
name string フィールド名
defaultValue unknown 任意。フィールドの初期値

Returns

const name = watch("name")

複数フィールドを監視する watch(names: string[], defaultValue?: {[key:string]: unknown}): unknown[]

レンダー関数の外で使われている複数フィールド(配列)を監視し、その変化を購読する。

Params

名前 説明
names string[] フィールド名の配列
defaultValue {[key: string]: unknown} 任意。フィールドの初期値をまとめたオブジェクト

Returns

const [name, name1] = watch(["name", "name1"])

フォーム全体を監視する watch(): {[key:string]: unknown}

useForm 内で onChange をトリガーに、フォーム全体の更新や変更を監視・購読し、再レンダリングを行う。

Returns

const formValues = watch()

watchフォームの値をリアルタイムで確認したいとき に使用する。
値を取得する方法としては getValues() などもあるが、フォームの値が変わるたびに自動で再レンダリングされれば問題はない。しかし、React Hook Form は 無駄な再レンダリングを抑える仕組み を持つため、getValues() で取得した値が常に最新とは限らない。そのため、入力中の最新値を確認したい場合は watch を使う必要がある。

const { register, getValues } = useForm();

const name = getValues("name"); // この時点の値しか取得できない

return (
  <div>
    <input {...register("name")} />
    <p>こんにちは、{name}さん</p> {/* 入力中は更新されない */}
  </div>
);

subscribe

subscribe: UseFormSubscribe<TFieldValues extends FieldValues>

フォームの状態や入力値の変更を購読できる。個別のフィールドだけ、またはフォーム全体を購読でき、フォームの変更による不要な再レンダリングを避けられる。

Props

名前 説明
name undefined / string[] 購読対象を指定。未指定でフォーム全体を購読。複数フィールド名も指定可能 subscribe()
subscribe({ name: ['firstName', 'lastName'] })
formState Partial<ReadFormState> 購読したい formState の項目を選択 subscribe({ formState: { values: true, isDirty: true, dirtyFields: true, touchedFields: true, isValid: true, errors: true, validatingFields: true, isValidating: true } })
callback Function 購読時に呼ばれるコールバック関数 subscribe({ formState: { values: true }, callback: ({ values }) => { console.log(values) } })
exact boolean input 名の購読を厳密一致にするかどうか subscribe({ name: 'target', exact: true })

useEffect を使って一度購読登録をすれば、非同期でフォームの変更に応じて自動的にコールバックが実行される。ただし、購読解除を忘れないこと。

import { useForm } from "react-hook-form"

type FormInputs = {
  firstName: string
  lastName: string
}

export default function App() {
  const { register, subscribe } = useForm<FormInputs>()

  useEffect(() => {
    // make sure to unsubscribe;
    const callback = subscribe({
      formState: {
        values: true,
      },
      callback: ({ values }) => {
        console.log(values)
      },
    })

    return () => callback()

    // You can also just return the subscribe
    // return subscribe();
  }, [subscribe])

  return (
    <form>
      <input {...register("firstName", { required: true })} />
      <input {...register("lastName", { required: true })} />
    </form>
  )
}

handleSubmit

</> handleSubmit: ((data: Object, e?: Event) => Promise<void>, (errors: Object, e?: Event) => Promise<void>) => Promise<void>

handleSubmit はフォームのバリデーションが成功した場合にフォームのデータを受け取る。

Props

名前 説明
SubmitHandler (data: Object, e?: Event) => Promise<void> バリデーションが成功したときに呼ばれるコールバック
SubmitErrorHandler (errors: Object, e?: Event) => Promise<void> バリデーションエラーが発生したときに呼ばれるコールバック

handleSubmit()フォーム送信時に必要な処理をまとめてやってくれる関数。内部で preventDefault() によるページリロード防止、入力値の取得、バリデーションの実行、成功/失敗時のコールバック呼び出しを自動で行う。これにより、自分で値取得やバリデーション処理を書く必要がなく、安全かつ簡潔にフォーム送信を扱える。

reset

reset: <T>(values?: T | ResetAction<T>, options?: Record<string, boolean>) => void

フォーム全体の状態、フィールドの参照、購読情報をまとめてリセットする関数。オプション引数を使うと、フォームの一部だけをリセットすることもできる。

Props

名前 説明
values object | (values: Object) => Object リセットするフォーム値のオブジェクト。指定する場合は全ての defaultValues を渡すことが推奨。
keepErrors boolean エラー状態を保持する。ただし、ユーザー操作により保証されなくなる場合あり。
keepDirty boolean dirtyFields の状態を保持し、isDirty も一時的に現在の状態のまま。
※フォーム入力値は反映されず、状態のみ保持。
keepDirtyValues boolean dirtyFieldsisDirty を保持し、未変更のフィールドのみ最新のリセット値で更新。
dirtyFields を反映させるには購読が必要。
keepValues boolean フォーム入力値を変更せずに保持。
keepDefaultValues boolean 初期化時の defaultValues を保持。
isDirty は新しい値と元の defaultValues を比較して再評価される。
dirtyFields も同様に更新される。
keepIsSubmitted boolean isSubmitted の状態を保持。
keepTouched boolean isTouched の状態を保持。
keepIsValid boolean isValid を一時的に現在の状態のまま保持。
keepSubmitCount boolean submitCount の状態を保持。

基本的にはフォーム送信後の初期化処理として使われる。

resetField

resetField: (name: string, options?: Record<string, boolean | any>) => void

特定のフィールドの状態だけをリセットする。

Props
この関数を呼び出すと、

  • isValid のフォーム状態が再評価される
  • isDirty のフォーム状態が再評価される
名前 説明
name string 登録済みのフィールド名
options.keepError boolean true にするとフィールドのエラー状態を保持する
options.keepDirty boolean true にすると dirtyFields を保持する
options.keepTouched boolean true にすると touchedFields 状態を保持する
defaultValue unknown 指定しない場合はフィールドが元の defaultValue に戻る。
指定した場合はフィールドの値をその値に更新し、`defa

全体をまとめてリセットするなら reset()、特定フィールドだけ更新したいときは resetField()を使う。

setError

setError: (name: string, error: FieldError, { shouldFocus?: boolean }) => void

setError は1つまたは複数のエラーを手動で設定することができる。

Props
整理すると以下の表になる。

名前 説明
name string 入力フィールドの名前
error { type: string, message?: string, types: MultipleFieldErrors } エラーの種類とメッセージを設定
config { shouldFocus?: boolean } エラー設定時に入力フィールドにフォーカスを当てるかどうか。
※ フィールドが登録されている場合のみ有効で、カスタム登録では動作しない

clearErrors

clearErrors: (name?: string | string[]) => void

clearError はフォーム内のエラーを手動でクリアすることができる。

Props

説明
undefined 全てのエラーを削除 clearErrors()
string 単一のエラーを削除 clearErrors("yourDetails.firstName")
string[] 複数のエラーを削除 clearErrors(["yourDetails.lastName"])

setValue

setValue: (name: string, value: unknown, config?: SetValueConfig) => void

setValueは、登録済みのフィールドの値を動的に設定できる。同時に、バリデーションを実行したりフォームの状態を更新するオプションもあり、不必要な再レンダリングをなるべく避けるように設計されている。

Props

名前 説明
name string 対象の単一フィールドまたはフィールド配列の名前
value unknown フィールドに設定する値。必須で undefined は不可
options.shouldValidate boolean フィールド単体のバリデーション状態(errors)やフォーム全体の有効性(isValid)を更新する。
指定したフィールドのみの touchedFields を更新する
options.shouldDirty boolean フィールド単体やフォーム全体の dirty 状態(dirtyFields / isDirty)を更新する。
指定したフィールドのみの dirtyFields を更新
options.shouldTouch boolean フィールド自体を touched としてマークする

setFocus

setFocus: (name: string, options: SetFocusOptions) => void

setFocus を使うと、プログラムから指定の入力フィールドにフォーカスを当てることができる。ただし、入力フィールドの ref が React Hook Form に登録されている必要がある。

Props

名前 説明
name string フォーカスを当てる入力フィールドの名前
options.shouldSelect boolean フォーカス時に入力内容を選択するかどうか

getValues

getValues: (payload?: string | string[]) => Object

getValues はフォームの値を取得するための最適化されたヘルパー関数。watch と違い、getValues は再レンダリングを引き起こさず、入力の変更に購読も行わない。

Props

名前 説明
fieldNames undefined フォーム全体の値を取得 (object)
fieldNames string 指定したパスの値を取得 (unknown)
fieldNames string[] 指定したパスの値を配列で取得 (unknown[])
config.dirtyFields boolean dirtyFields のみを取得
config.touchedFields boolean touchedFields のみを取得

getFieldState

getFieldState: (name: string, formState?: Object) => ({isDirty, isTouched, invalid, error})

getFieldState は個別のフィールド状態を取得できる。型安全にネストしたフィールドの状態を取得したい場合に便利。

Props

名前 説明
name string 登録済みのフィールド名
formState object オプション。useFormuseFormContextuseFormState から formState を読み取っていない場合に指定が必要。詳細はルールを参照

Return

名前 説明 条件
isDirty boolean フィールドが変更されたかどうか dirtyFields を購読していること
isTouched boolean フィールドがフォーカス・ブラーされたかどうか touchedFields を購読していること
invalid boolean フィールドが有効でないかどうか errors を購読していること
error undefined | FieldError フィールドのエラーオブジェクト errors を購読していること

getFieldState の引数 formState は、useFormuseFormContextuseFormState などを使用していない場合にのみ指定するもの。通常は指定不要。

戻り値の hogehoge を購読するには、formStateuseFormState を使って、dirtyFieldstouchedFieldserrors など必要なプロパティにアクセスすると、その状態が購読される。

const { register, formState } = useForm();
const { dirtyFields, touchedFields, errors } = formState;
const { dirtyFields, touchedFields, errors } = useFormState({
  name: "yourDetails.firstName" // 特定フィールドだけ購読可能
});

trigger

trigger: (name?: string | string[]) => Promise<boolean>

trigger を使うことで、フォームや入力フィールドのバリデーションを手動で実行できる。また、入力のバリデーションが他の入力値に依存している場合にも便利。

Props

名前 説明
name undefined 全フィールドのバリデーションを実行 trigger()
name string 指定したフィールドのバリデーションを実行 trigger("yourDetails.firstName")
name string[] 複数フィールドのバリデーションを実行 trigger(["yourDetails.lastName"])
shouldFocus boolean バリデーション時に入力にフォーカスするか。入力が登録されている場合のみ有効 trigger('name', { shouldFocus: true })

control

control: Object

このオブジェクトには、コンポーネントを React Hook Form に登録するためのメソッドが含まれている。

control はフォームフィールドの状態管理の中心。値の取得・更新・バリデーション・購読 などを内部で一括管理している。普通の register は単純な input で使えるが、カスタムコンポーネントや複雑な UI では control を使うことになる

useController

useController:
(props?: UseControllerProps) => { field: object, fieldState: object, formState: object }

このカスタムフックは Controller と同じ仕組みで動くフックで、同じプロパティやメソッドが使える。つまり、このフックを使うと、何度も使える『値を管理できる入力フォーム』を簡単に作れる。

Props

名前 必須 説明
name FieldPath 入力フィールドの一意な名前
control Control useForm を呼び出したときに得られるコントロールオブジェクト。FormProvider 使用時は省略可能
rules Object register と同じ形式でバリデーションルールを指定できる。
例:required, min, max, minLength, maxLength, pattern, validate
shouldUnregister boolean デフォルト値 false。アンマウント時に入力を登録解除し、defaultValues も削除する。
useFieldArray と併用する場合は注意(アンマウント・再マウント後に unregister が呼ばれるため)
disabled boolean デフォルト値 falsefield から返される disabled。コントロールされた入力が無効化され、送信データから値が除外される
defaultValue unknown useFormdefaultValues またはフィールド単位で指定する必要がある。undefined は無効。
フォームで reset を使う場合は useFormdefaultValues を渡す必要がある

Return

オブジェクト プロパティ 説明
field onChange (value: any) => void 入力値をライブラリに送る関数。入力コンポーネントの onChange に割り当てる。valueundefined にしてはいけない。手動で setValue などを呼ぶ必要はない。
field onBlur () => void 入力の onBlur イベントをライブラリに送る関数。入力コンポーネントの onBlur に割り当てる。
field value unknown コントロールされたコンポーネントの現在の値。
field disabled boolean 入力の無効状態。
field name string 登録された入力の名前。
field ref React.Ref 入力に ref を渡して Hook Form に接続。エラー入力にフォーカス可能にする。
fieldState invalid boolean 現在の入力が無効かどうか。
fieldState isTouched boolean 入力が触れられたかどうか。
fieldState isDirty boolean 入力が変更されたかどうか。
fieldState error object この入力のエラー情報。
formState isDirty boolean ユーザーが入力を変更した後に true になる。全フィールドのデフォルト値を useForm で設定しておく必要あり。
formState dirtyFields object ユーザーが変更したフィールドのオブジェクト。全フィールドの defaultValues が必要。
formState touchedFields object ユーザーが触れた入力フィールドのオブジェクト。
formState defaultValues object useForm で設定したデフォルト値、または reset で更新した値。
formState isSubmitted boolean フォームが送信された後に true になる。reset 呼び出しまで保持。
formState isSubmitSuccessful boolean フォームがランタイムエラーなく正常に送信されたか。
formState isSubmitting boolean 現在フォームが送信中かどうか。
formState isLoading boolean 非同期 defaultValues をロード中かどうか。
formState submitCount number フォームが送信された回数。
formState isValid boolean フォームにエラーがない場合 truesetError は影響しない。
formState isValidating boolean バリデーション中に true
formState validatingFields object 非同期バリデーション中のフィールド情報。
formState errors object フィールドごとのエラー情報。ErrorMessage コンポーネントでも取得可能。
formState disabled boolean useFormdisabled プロパティでフォーム全体を無効化した場合に true

useController は React Hook Form で カスタム入力コンポーネントをフォームに接続するためのフック。通常の register は HTML 標準の入力向けで、値の登録しかできないが、useController は入力値・フォーカス状態・バリデーション状態を細かく管理できる。

これにより、Material-UI(MUI)や shadcn/ui のようなカスタムコンポーネントでも、内部の refonChange を意識せずにフォームと同期できる。例えば MUI の TextFieldfield を渡すだけで、値の変更やエラー表示、フォーカス制御が自動で行われる。

import { TextField } from "@material-ui/core";
import { useController, useForm } from "react-hook-form";


function Input({ control, name }) {
  const {
    field,
    fieldState: { invalid, isTouched, isDirty },
    formState: { touchedFields, dirtyFields }
  } = useController({
    name,
    control,
    rules: { required: true },
  });


  return (
    <TextField 
      onChange={field.onChange} // send value to hook form 
      onBlur={field.onBlur} // notify when input is touched/blur
      value={field.value} // input value
      name={field.name} // send down the input name
      inputRef={field.ref} // send input ref, so we can focus on input when error appear
    />
  );
}

まとめると、useController複雑なカスタム入力でも簡単に React Hook Form に接続できる仕組みで、UI ライブラリを安全かつ効率的に使うための必須ツールになる。

Controller

Controller: Component

React Hook Form は基本的にアンコントロールコンポーネント(ネイティブ入力)を前提に設計されているが、実際には React-Select や AntD、MUI などの外部のコントロールコンポーネント を使わざるを得ないことが多い。このラッパーコンポーネントを使うと、そうした外部コンポーネントと React Hook Form を 簡単に組み合わせて使えるようになる。

Props

名前 必須 説明
name FieldPath 入力フィールドの一意な名前
control Control useForm から取得するコントロールオブジェクト。FormProvider 使用時は任意
render Function レンダープロップ。React 要素を返し、onChangeonBlurnamerefvalue を子コンポーネントに渡せる。外部のコントロールコンポーネントとの統合を簡単にする。fieldState も提供される
rules Object バリデーションルール。register と同じ形式で指定可能(requiredminmaxminLengthmaxLengthpatternvalidate など)
shouldUnregister boolean = false アンマウント時に入力を登録解除し、defaultValues も削除する。useFieldArray 使用時は注意(再マウント・並び替えで unregister が呼ばれるため)
disabled boolean = false field から返される。コントロール入力が無効化され、送信データから値が除外される
defaultValue unknown undefined は使用不可。フィールドレベルで設定するか、useFormdefaultValues を使用する必要がある。リセット時も defaultValues を渡す必要がある。onChangeundefined を渡すのは不可で、代わりに null または空文字を使用する

Return

オブジェクト プロパティ 説明
field onChange (value: any) => void 入力値を React Hook Form に送る関数。onChange に割り当て、値は undefined にしない。フォーム状態を更新するので、setValue などを手動で呼ぶ必要はない
field onBlur () => void 入力の onBlur イベントを RHF に送る関数。onBlur に割り当てる
field value unknown コントロールされたコンポーネントの現在の値
field disabled boolean 入力の無効状態
field name string 登録された入力の名前
field ref React.ref 入力とフォームを接続するための ref。エラー時にフォーカスを当てるために割り当てる
fieldState invalid boolean 現在の入力が無効かどうか
fieldState isTouched boolean 現在の入力がタッチされたかどうか
fieldState isDirty boolean 現在の入力が変更されたかどうか
fieldState error object この入力特有のエラー情報
formState isDirty boolean ユーザーがフォームのどれかの入力を変更したかどうか。全入力の defaultValuesuseForm に渡すことが重要
formState dirtyFields object ユーザーが変更したフィールドのオブジェクト。useForm に全入力の defaultValues を渡すことが重要
formState touchedFields object ユーザーが操作した全入力のオブジェクト
formState defaultValues object useFormdefaultValues または reset による更新値
formState isSubmitted boolean フォームが送信された後に true になる。reset 呼び出しまで保持
formState isSubmitSuccessful boolean フォームがランタイムエラーなしで正常に送信されたかどうか
formState isSubmitting boolean 現在フォームが送信中かどうか
formState isLoading boolean 非同期の defaultValues を読み込んでいる場合 true
formState submitCount number フォームが送信された回数
formState isValid boolean フォームにエラーがない場合 true。setError は影響しない
formState isValidating boolean バリデーション中に true
formState errors object フィールドごとのエラーオブジェクト。ErrorMessage コンポーネントで簡単に取得可能

useController はフックで、自分で field を受け取り JSX に値やイベントを適用するための柔軟な方法。Controller はコンポーネントで、render プロップに入力を渡すだけで簡単に接続できる。要するに、細かく制御したいときは useController、手軽に接続したいときは Controller を使う。

useFormContext

useFormContext: Function

useFormContext はフォームのコンテキストにアクセスするためのものであり、コンテキストを毎回プロップとして渡すのが面倒になるような 深くネストされたコンポーネント構造 で使うことを想定している。

import { useForm, FormProvider, useFormContext } from "react-hook-form"


export default function App() {
  const methods = useForm()
  const onSubmit = (data) => console.log(data)
  const { register, reset } = methods


  useEffect(() => {
    reset({
      name: "data",
    })
  }, [reset]) // ❌ never put `methods` as the deps


  return (
    <FormProvider {...methods}>
      // pass all methods into the context
      <form onSubmit={methods.handleSubmit(onSubmit)}>
        <NestedInput />
        <input {...register("name")} />
        <input type="submit" />
      </form>
    </FormProvider>
  )
}


function NestedInput() {
  const { register } = useFormContext() // retrieve all hook methods
  return <input {...register("test")} />
}

FormProvider

FormProvider: Component

FormProvider はフォームのコンテキストオブジェクトを保持し、子コンポーネントがそのコンテキストを利用して useForm のプロップやメソッドにアクセスできるようにする。

Props

名前 説明
...props Object FormProvider には useForm のすべてのメソッドが必要

FormProvider がコンテキストを渡し、useFormContext がそれを受け取って使う

useWatch

useWatch: ({ control?: Control, name?: string, defaultValue?: unknown, disabled?: boolean }) => object

watch API と似た動作をするが、このカスタムフックでは 再レンダリングをフック単位で分離 できる。そのため、アプリケーションのパフォーマンスが向上する可能性がある。

Props

名前 説明
name string | string[] | undefined 監視するフィールドの名前
control Object useForm から提供されるコントロールオブジェクト。FormProvider 使用時は任意
compute function 選択的・計算されたフォーム値を監視できる関数。フォーム全体を監視して特定条件で値を返したり、特定フィールドだけを監視可能。
例1: 全体を監視して条件付きで値を返す
例2: 特定フィールドだけを監視して計算して返す
defaultValue unknown 初回レンダー前に useWatch が返すデフォルト値。指定すると最初のレンダーでは必ずこの値が返る
disabled boolean = false 監視を無効化するオプション
exact boolean = false 入力名の完全一致で監視するかどうか

Return

戻り値
useWatch({ name: 'inputName' }) unknown
useWatch({ name: ['inputName1'] }) unknown[]
useWatch() { [key: string]: unknown }

watch は値が変わると その値を使うコンポーネント全体が再レンダリング される。一方、useWatch監視する値だけを別コンポーネントやフックで管理するため、その値を使う部分だけが更新され、フォーム全体は再描画されない。パフォーマンスを意識した効率的な監視方法と言える。

import { useForm, useWatch } from "react-hook-form"


interface FormInputs {
  firstName: string
  lastName: string
}


function FirstNameWatched({ control }: { control: Control<FormInputs> }) {
  const firstName = useWatch({
    control,
    name: "firstName", // without supply name will watch the entire form, or ['firstName', 'lastName'] to watch both
    defaultValue: "default", // default value before the render
  })


  return <p>Watch: {firstName}</p> // only re-render at the custom hook level, when firstName changes
}


function App() {
  const { register, control, handleSubmit } = useForm<FormInputs>()


  const onSubmit = (data: FormInputs) => {
    console.log(data)
  }


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>First Name:</label>
      <input {...register("firstName")} />
      <input {...register("lastName")} />
      <input type="submit" />


      <FirstNameWatched control={control} />
    </form>
  )
}

Watch

Watch: Component

useWatch と同じ機能を持つ コンポーネント版の React Hook Form。フックを別のコンポーネント内で使う代わりに、JSX 内で直接 <Watch /> を使うことで、フォームの値を監視して表示できる。

Props

名前 説明
name string | string[] | undefined 監視するフィールドの名前
control Object useForm から提供されるコントロールオブジェクト。FormProvider 使用時は任意
compute function 選択的・計算されたフォーム値を監視できる関数。
フォーム全体を監視して条件付きで値を返したり、特定フィールドだけを監視可能
defaultValue unknown 初回レンダー前に返すデフォルト値。指定すると最初のレンダーでは必ずこの値が返る
disabled boolean = false 監視を無効化するオプション
exact boolean = false 入力名の完全一致で監視するかどうか
render Function 指定したフィールドを監視し、値が変わるたびに子関数を再レンダリングする。JSX 内で宣言的にフォーム値を利用できる

useFormState

useFormState: ({ control: Control }) => FormState

useFormState は、フォームの状態ごとに購読でき、再レンダリングをそのカスタムフックの範囲だけに限定できる。購読するフォーム状態のスコープが明確なため、他の useFormStateuseForm に影響を与えない。
複雑で大規模なフォームアプリケーションにおいて、再レンダリングの負荷を抑えるのに役立つ。

Props

名前 説明
control Object useForm が提供する control オブジェクト。FormProvider を使用している場合は省略可能。
name string / string[] 特定のフィールド名、または複数のフィールド名を指定できる。未指定の場合は全フィールドの formState 更新を購読する。
disabled boolean = false 購読を無効にするためのオプション。
exact boolean = false 指定したフィールド名との一致を厳密に判定するオプション。

Returns

名前 説明
isDirty boolean ユーザーがいずれかの入力を変更した後に true になる。useForm で全フィールドの defaultValues を提供する必要がある。例:setValue('test', 'change') で true、setValue('test', '') では false。ファイル入力はキャンセル可能なためアプリ側で管理が必要。カスタムオブジェクトや Class、File オブジェクトはサポートされない。
dirtyFields object ユーザーが変更したフィールドのオブジェクト。全フィールドの defaultValues が必要。isDirty とは異なり、フィールド単位での状態を示す。
touchedFields object ユーザーが操作したすべての入力フィールドのオブジェクト。
defaultValues object useFormdefaultValues に設定された値、または reset API によって更新された値。
isSubmitted boolean フォーム送信後に true になり、reset が呼ばれるまで保持される。
isSubmitSuccessful boolean フォームがランタイムエラーなく正常に送信されたかを示す。
isSubmitting boolean 現在フォームが送信中の場合は true、それ以外は false。
isLoading boolean 現在フォームが非同期の defaultValues を読み込み中の場合に true。例:defaultValues: async () => await fetch('/api')
submitCount number フォームが送信された回数。
isValid boolean フォームにエラーがない場合に true。setError では影響を受けず、常にバリデーション結果から算出される。
isValidating boolean バリデーション中は true。
validatingFields object 非同期バリデーション中のフィールドを示す。
errors object フィールドごとのエラーを格納するオブジェクト。ErrorMessage コンポーネントで簡単に取得可能。
disabled boolean useFormdisabled プロパティでフォームが無効化されている場合は true。

React Hook Form では formState が内部的に React の state として管理されているため、どこかの入力値が変わると formState を参照しているコンポーネント全体が再レンダリングされる。この挙動は小さなフォームでは問題にならないが、フィールドが多い大規模フォームではパフォーマンスに影響する。useFormState は特定のフィールドや状態だけを購読する仕組みを提供しており、必要な部分だけを再レンダリングできるため、効率的にフォーム状態を管理できる。

import { useForm, useFormState } from "react-hook-form"


function Child({ control }) {
  const { dirtyFields } = useFormState({ control })


  return dirtyFields.firstName ? <p>Field is dirty.</p> : null
}


export default function App() {
  const { register, handleSubmit, control } = useForm({
    defaultValues: {
      firstName: "firstName",
    },
  })
  const onSubmit = (data) => console.log(data)


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} placeholder="First Name" />
      <Child control={control} />


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

ErrorMessage

ErrorMessage: Component

関連する入力フィールドのエラーメッセージを表示するシンプルなコンポーネント

Props

名前 必須 説明
name string フィールド名
errors object React Hook Form のエラーオブジェクト。FormProvider を使用している場合は省略可能
message string | React.ReactElement インラインで表示するエラーメッセージ
as React.ElementType | string エラーメッセージのラッパーコンポーネントや HTML タグ。例:as="span" や `as={}。
render ({ message: string | React.ReactElement, messages?: Object }) => any エラーメッセージや複数メッセージを描画するための render prop。criteriaMode'all' に設定する必要がある
シングルエラーメッセージ
import { useForm } from "react-hook-form"
import { ErrorMessage } from "@hookform/error-message"


interface FormInputs {
  singleErrorInput: string
}


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


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("singleErrorInput", { required: "This is required." })}
      />
      <ErrorMessage errors={errors} name="singleErrorInput" />


      <ErrorMessage
        errors={errors}
        name="singleErrorInput"
        render={({ message }) => <p>{message}</p>}
      />


      <input type="submit" />
    </form>
  )
}
複数のエラーメッセージ
import { useForm } from "react-hook-form"
import { ErrorMessage } from "@hookform/error-message"


interface FormInputs {
  multipleErrorInput: string
}


export default function App() {
  const {
    register,
    formState: { errors },
    handleSubmit,
  } = useForm<FormInputs>({
    criteriaMode: "all",
  })
  const onSubmit = (data: FormInputs) => console.log(data)


  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("multipleErrorInput", {
          required: "This is required.",
          pattern: {
            value: /d+/,
            message: "This input is number only.",
          },
          maxLength: {
            value: 10,
            message: "This input exceed maxLength.",
          },
        })}
      />
      <ErrorMessage
        errors={errors}
        name="multipleErrorInput"
        render={({ messages }) =>
          messages &&
          Object.entries(messages).map(([type, message]) => (
            <p key={type}>{message}</p>
          ))
        }
      />


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

useFieldArray

useFieldArray: UseFieldArrayProps

フィールド配列(動的フォーム)を扱うためのカスタムフック。ユーザー体験とパフォーマンスを向上させることを目的としている。

Props

名前 必須 説明
name string フィールド配列の名前。動的な名前はサポートされない。
control Object useForm から提供されるコントロールオブジェクト。FormProvider を使用している場合は省略可能。
shouldUnregister boolean コンポーネントがアンマウントされたときにフィールド配列を登録解除するかどうか。
keyName string 自動生成された識別子を key プロップとして使う際の属性名。デフォルトは id。次のメジャーバージョンで廃止予定。
rules Object register と同じバリデーションルール API(requiredminLengthmaxLengthvalidate)を使用可能。組み込みバリデーションの場合、エラーは formState.errors?.fieldArray?.rootFieldError 型で追加される。例:useFieldArray({ rules: { minLength: 4 } })

Return

名前 型 / シグネチャ 説明
fields object & { id: string } コンポーネントの defaultValuekey を含むオブジェクト。
append (obj: object | object[], focusOptions) => void フィールドの末尾に入力を追加してフォーカス。追加時に値は自動登録される。データは必須で部分的では不可
prepend (obj: object | object[], focusOptions) => void フィールドの先頭に入力を追加してフォーカス。追加時に値は自動登録される。データは必須で部分的では不可
insert (index: number, value: object | object[], focusOptions) => void 指定位置に入力を挿入してフォーカス。データは必須で部分的では不可
swap (from: number, to: number) => void フィールドの位置を入れ替える。
move (from: number, to: number) => void フィールドを別の位置に移動する。
update (index: number, obj: object) => void 指定位置の入力を更新。更新されたフィールドはアンマウント・再マウントされる。不要な場合は setValue API を使用。データは必須で部分的では不可
replace (obj: object[]) => void フィールド配列全体の値を置き換える。
remove (index?: number | number[]) => void 指定位置の入力を削除。インデックス未指定の場合は全削除。

useFieldArray は React Hook Form で配列型の入力を動的に管理するためのフックで、行の追加・削除・並び替えやバリデーションを簡単に扱える。例えばユーザーリストのフォームでは、fields で現在の配列を取得し、append で新しい行を追加、remove で削除できる。各入力は register で配列のインデックスを指定して登録することで、送信時にまとめて取得できる。

const { control, register, handleSubmit } = useForm({ defaultValues: { users: [{ name: "", age: 0 }] } });
const { fields, append, remove } = useFieldArray({ name: "users", control });

<form onSubmit={handleSubmit(data => console.log(data))}>
  {fields.map((field, i) => (
    <div key={field.id}>
      <input {...register(`users.${i}.name`)} placeholder="Name" />
      <input {...register(`users.${i}.age`)} placeholder="Age" />
      <button type="button" onClick={() => remove(i)}>削除</button>
    </div>
  ))}
  <button type="button" onClick={() => append({ name: "", age: 0 })}>追加</button>
  <button type="submit">送信</button>
</form>
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?