💨

Next13のServer Actionsを使ってReact Hook Formとyupを使ってみる

2023/09/21に公開

事の発端

Next.jsのバージョンが13になって今までのNextからガラッと作り方が変わりそうと思ってたところに、「APIがいらなくなる」という話を耳にした。
なにやらServer Actionsなるものがあるらしい。

Server Actions

Next13.4はまだα版。
機能改善などは行われていたが、13.5でもα版から移行したことなどは書かれていなかった。
https://nextjs.org/blog/next-13-4#server-actions-alpha
https://nextjs.org/blog/next-13-5

何はともあれ作ってみる

  • Next13
  • typescript
  • react-hook-form
  • yup
  • prisma

prismaでcreateするとこまで。
※仕様一部省略 + type指定簡略化

Nextのプロジェクト作る

ドキュメントに沿ってプロジェクト作成。
https://nextjs.org/docs/getting-started/installation

npx create-next-app@latest

色々質問されるから自分の好みに合わせて作成。
configにserveraAtionsの設定を追加。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true,
  },
  reactStrictMode: true,
};

module.exports = nextConfig;

prismaの準備

DBは簡単にDockerでpostgreを立ち上げた。

# とりあえず初期化
pnpm prisma init
# schemaをいじってマイグレーション
pnpm prisma migrate dev --name init
# generateする
pnpm prisma generate

ここらへん忘れずにやってとりあえずオッケー。
今回メインにやりたいとこでないのでパスワードとかも全部簡略化して作る。

model Users {
 id           String    @id @default(uuid())
 name         String
 email        String
 password     String
 createdAt    DateTime  @default(now()) @map("created_at")
 updatedAt    DateTime  @updatedAt @map("updated_at")
}

登録フォーム作る

最近はfeaturesに機能ごとまとめることが多いのでfeatures/userディレクトリに作っていく。

src/
├─ app/
├─ features/
│  ├─ user/
│  │  ├─ ココ
├─ components/
# ...割愛

外側

今回はコンポーネント化もせずにそのまま作っちゃう。

features/user/UserForm.tsx
'use client'
import { yupResolver } from '@hookform/resolvers/yup'
import { FC } from 'react'
import { useForm } from 'react-hook-form'
import { createUser } from '~/features/user/hooks/create'
import { userSchema } from '~/schemas/user'

const UserForm: FC = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    mode: 'onSubmit',
    resolver: yupResolver(userSchema),
  })

  const onSubmit = handleSubmit((data) => {
    createUser(data)
  })

  return (
    <form className='flex flex-col gap-4' onSubmit={onSubmit}>
      <section className='flex flex-col'>
        <label className=''>name</label>
        <input className='rounded-md border border-gray-500 px-2 py-1' {...register('name')} />
        {errors.name && <span className='text-xs text-red-500'>{errors.name.message}</span>}
      </section>
      <section className='flex flex-col'>
        <label className=''>email</label>
        <input className='rounded-md border border-gray-500 px-2 py-1' {...register('email')} />
        {errors.email && <span className='text-xs text-red-500'>{errors.email.message}</span>}
      </section>
      <section className='flex flex-col'>
        <label className=''>password</label>
        <input className='rounded-md border border-gray-500 px-2 py-1' {...register('password')} />
        {errors.password && <span className='text-xs text-red-500'>{errors.password.message}</span>}
      </section>
      <button type='submit' className='text-bold rounded-sm bg-orange-300 px-2 py-1'>
        Add User
      </button>
    </form>
  )
}
export default UserForm
app/page.tsx
"use client";
import { NextPage } from "next";
import UserForm from "~/features/user/UserForm";

const Home: NextPage = () => {
  return (
    <main>
      <UserForm />
    </main>
  );
};

export default Home;

こんな感じ。
image
バリデーションできてるしいい感じ。
image

登録

今回のメインどころ。

prisma使ってDBへの登録処理するところ。
ここでAPIを使う必要がなくなるのはとても便利になった印象。
ただprismaのコードがあると違和感がすごいなぁ……本当に動くのか……。

features/user/hooks/create.ts
'use server'
import prisma from '~/lib/prisma'

export const createUser = async (data: { name: string; email: string; password: string }) => {
  'use server'
  console.log(data)
  await prisma.users.create({ data })
}

手間取ったときの参考にしたところ。
https://github.com/react-hook-form/react-hook-form/issues/10391

登録してみる

適当に入力してボタンぽち。
image
console.log出てる。

{ name: 'テスト', email: 'sample@email.com', password: 'passpass' }

DB見てみる。
image
おお、登録できた。
パスワードやらそのまま入れてるけど今回はこれでオッケーってことにする。

やってみて

結論:
とりあえずreact-hook-formとyupは動きはするっぽい。
Next13はまだ触りたてなのもあってclient側なのかserver側なのかの切り分けが難しいのと、formのaction使ってなかったりで動いてはいるけど実際想定通りの動きなのかはまた調べないといけない(一応サーバー側では動いてたけど)。
actionを使った実装だとどうなるのか、reack-hook-formをこれからも使っていくのであればよりドキュメントに沿って作れるようにこれからも試していく。

Discussion