13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GLOBISAdvent Calendar 2023

Day 6

react-hook-formのuseFormとuseFormContextの違い

Last updated at Posted at 2023-12-05

これは GLOBIS Advent Calendar 2023 6日目の記事です。

あいさつ

ご縁あってグロービスさんに参画させていただいています。
よしにゃんです。
アドベントカレンダーに参加させていただきます。

@hackpopo さんからバトンを受け取り、書いています。

この記事を書くモチベーション

実務で初めてreactとreact-hook-formを使っているのですが、useFormとuseFormContextの関係というか使い分けがあんまり分かりませんでした。
公式ドキュメントを見ても端的に書かれているので、僕には理解できなかったです。
同じように思っているひとがいるかもしれないと記事を書いています。

react-hook-formとは

ぱっと読んだ感じ、formまわりの処理を簡潔にし、パフォーマンス(実行速度?)をあげるためのものです。

生reactで実直に書くよりもreact-hook-formを使うことで処理を簡潔に隠蔽してくれてそうですね。
他に、いいライブラリがあったらコメント欄で教えてください。

useFormとuseFormContextとは

react-hook-formのAPIです。
フォーム周りのカスタムフックですね。

結論

useFormはformの状態管理をしている。
useForm単独でも使用可能だが、複雑なformの場合は、useFormContextを使い、useFormの状態やメソッドをuseFormContextを通じて子コンポーネントに渡すことができる。

使いわけの簡単なコツ

Formが単一コンポーネントで済むような規模ならuseFormのみを使う。
Formが複数のコンポーネントにまたがる場合、useFormContextを使って複数のコンポーネントに分けるとことで、ソースの見通しを良くし、小さく作り、再利用しやすくする。

使い方

useFormを定義した親コンポーネントからFormProviderで子コンポーネントを囲むことで子コンポーネントでメソッドなどが使用可能となる。子コンポーネントではuseFormContextから状態やメソッドをもらって使用する。

サンプルコード

実践的な内容にしたいので、typescriptで型定義・バリデーション定義にzodを使っています。
input要素はひとつなので、そこはシンプルになっています。

Formが単一コンポーネントで済む場合

src/App.tsx
// 親の親コンポーネント(参考までに書きました)
import React from 'react'
import { SampleForm } from './SampleForm'

const App = () => {
  return (
      <div>
        <SampleForm />
      </div>
  )
}

export default App
src/SampleForm.tsx
// 親コンポーネントのみ
import * as react from "react";
import { useForm, SubmitHandler } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from 'zod'

const userSchema = z.object({
    name: z.string().min(1, '名前を入力してください')
})

type UserData = z.infer<typeof userSchema>

export const SampleForm:React.FC = () => {
    const  { register, handleSubmit, formState: { errors } } = useForm<UserData>({
        resolver: zodResolver(userSchema)
    })

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

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <div>
                <label htmlFor="name">なまえ</label>
                <input id="name" {...register('name')} />
                <p>{errors.name?.message}</p>
            </div>
            <button type="submit">送信</button>
        </form>
    )
}

単純なformなのでuseFormのみとなっています。

Formが複数のコンポーネントにまたがる場合

src/SampleForm.tsx
// 親コンポーネント
import * as react from "react";
import {useForm, SubmitHandler, FormProvider} from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from 'zod'
import NameInput from "./NameInput";

const userSchema = z.object({
    name: z.string().min(1, '名前を入力してください')
})

export type UserData = z.infer<typeof userSchema>

export const SampleForm:React.FC = () => {
    const  methods = useForm<UserData>({
        resolver: zodResolver(userSchema)
    })

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

    return (
        <FormProvider {...methods}>
            <form onSubmit={methods.handleSubmit(onSubmit)}>
                <NameInput />
                <button type="submit">送信</button>
            </form>
        </FormProvider>
    )
}
src/NameInput.tsx
// 子コンポーネント
import * as react from "react";
import { useFormContext } from "react-hook-form";
import { UserData } from "./SampleForm"
import React from "react";

const NameInput: React.FC = () => {
    const { register, formState: { errors } } = useFormContext<UserData>()

    return (
        <div>
            <label htmlFor="name">なまえ</label>
            <input id="name" {...register('name')} />
            <p>{errors.name?.message}</p>
        </div>
    )
}

export default NameInput

FormProviderを通じて、子コンポーネント(NameInput)にuseFormが持つメソッドを渡して、子でも使えるようにしています。
今回は簡素ですが、数が多いformを作るときにはuseFormContextを使うと良いでしょう。

手元と動かしたい人のためのコマンドメモ

npx create-react-app sample-react-hook-form --template typescript
cd sample-react-hook-form
npm install react-hook-form zod @hookform/resolvers
npm start

僕の解説が端的すぎて、もしかするとコードの意味がわかりづらいかもしれませんが、本記事のタイトルの範囲で解説しました。

より詳しく学びたいひと向けの参考文献

react公式
useContext – React

useFormContext内部でuseContextが使われているようです
react-hook-form/src/useFormContext.tsx

デザインパターン
Provider Pattern

最後に

最後まで目を通していただきありがとうございます。
殺傷能力をデバフしたまさかりや、コメント大歓迎です。
よかったらいいね、ストック、拡散もお願いします。

次の人へのバトン

明日は、@g-ssさんの記事です。よろしくお願いします。

13
5
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
13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?