Closed8

NextJSで、react-hook-formのフォームにreCAPTCHA V2を導入する

かいとかいと

Yup × react-hook-formで実装しているフォームにreCAPTCHAを導入したい
前提として、以下を想定している

  • フロント側でreCAPTCHAの認証を行い、tokenを保存する
  • Submit時にtokenをバックエンドに送る
  • バックエンドでトークンを検証する
    • 問題があればBad Requestを返す
    • 問題がなければ保存する

Submitまでを検証する

まずはNextJSのプロジェクトを作成して、簡単なフォームを作成する

terminal
$ pnpm create next-app
✔ What is your project named? … test
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … Yes
✔ What import alias would you like configured? … @/*

terminal
$ pnpm add yup react-hook-form @hookform/resolvers
terminal
pnpm dev
=> open localhost:3000
かいとかいと

react-hook-formの例を参考にフォームを追加する

page.tsx
'use client'
import { useForm } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import * as yup from "yup"
import { NextPage } from "next"

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

const Page: NextPage = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(schema),
  })
  const onSubmit = (data) => console.log(data)

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

      <p>age</p>
      <input {...register("age")} />
      <p style={{color: 'red'}}>{errors.age?.message}</p>

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

export default Page

参考

https://react-hook-form.com/get-started#SchemaValidation

かいとかいと

reCAPTCHAを表示するライブラリを入れる

terminal
$ pnpm add react-google-recaptcha

以下が基本実装
reCAPTCHAのセットアップで取得したsitekeyをいれる
チェックを完了したら、onChangeが発火するようです。

page.tsx
'use client'
import ReCAPTCHA from "react-google-recaptcha";

  const onChange = (value: string) => {
    console.log("Captcha value:", value);
  }

---

  <ReCAPTCHA
    sitekey="your site key"
    onChange={onChange}
  />

かいとかいと

ここまででフォームにreCAPTCHAのComponentが表示される

$ pnpm dev
=> https://localhost:3000

page.tsx
  const onChange = (value: string) => {
    console.log("Captcha value:", value);
  }

チェックボタンを通すことで
上記の実装部分のvalueにトークンが返ることを確認できる

かいとかいと

react-hook-formにreCAPTCHAのトークンを載せる

onChangeの発火時にreact-hook-formに載せようと思う。
setValueを使ってreCAPTCHA用のkeyにセットします。

実装

以下の実装で、tokenのkeyに保存することができた

schema

token: yup.string().required()

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

onChange

setValue("token", value);

page.tsx(onChange)
  const onChange = (value: string) => {
    setValue("token", value);
    console.log("Captcha value:", value);
  }

参考

https://react-hook-form.com/docs/useform/setvalue

かいとかいと

チェックできていないとエラーが出ることを確認した
また、タイムアウトでチェックが外れたときもrequiredでエラーになることが確認できた

かいとかいと

実装のすべて

page.tsx
'use client'
import { useForm } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import * as yup from "yup"
import { NextPage } from "next"
import ReCAPTCHA from "react-google-recaptcha";


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

const Page: NextPage = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
    setValue
  } = useForm({
    resolver: yupResolver(schema),
  })

  const onChange = (value: string) => {
    setValue("token", value);
    console.log("Captcha value:", value);
  }
  const onSubmit = (data) => console.log(data)

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

      <p>age</p>
      <input {...register("age")} />
      <p style={{color: 'red'}}>{errors.age?.message}</p>

      <ReCAPTCHA
        sitekey="your site key"
        onChange={onChange}
      />
       <p style={{color: 'red'}}>{errors.token?.message}</p>
      <input type="submit" />
    </form>
  )
}

export default Page

このスクラップは13日前にクローズされました