Server Actionsのエラー処理【2つの方法を紹介/Next.js】
2024.10.212024.10.23
この記事は約3分で読めます
この記事の筆者:三好アキ
🔹 専門用語なしでプログラミングを教えるメソッドに定評があり、1200人以上のビギナーを、最新のフロントエンド開発入門に成功させる。
🔹 Amazonベストセラー1位を複数回獲得している『はじめてつくるReactアプリ with TypeScript』著者。
Amazon著者ページはこちら → amazon.co.jp/stores/author/B099Z51QF2
React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて無料配信中。登録はこちらから → 無料メルマガ登録
Server Actionsのエラー処理方法
Next.jsのServer Actionsでは、returnしたvalue(戻り値)の扱い方にややクセがあります。
たとえば下記のようなServer Actionsのコードを考えてみましょう。
フロントエンドのユーザー登録画面に入力された「名前」「メールアドレス」「パスワード」を、データベースに書き込むServer Actionsのコードです。
"use server"
import { redirect } from "next/navigation"
import connectDB from "../utils/database"
import { UserModel } from "../utils/schemaModels"
export const userRegister = async (formData) => {
const userData = {
name: formData.get("name"),
email: formData.get("email"),
password: formData.get("password"),
}
try{
await connectDB()
await UserModel.create(userData)
}catch{
return {message: "エラー:ユーザー登録失敗"}
}
redirect("/")
}
このコードの挙動を説明しましょう。
try
の中のコードでデータベースへの書き込みが成功すれば、redirect("/")
でトップページへと移動しますが、もし途中でなんらかのエラーが発生した場合にはcatch
内のコードが実行される、という挙動です。
ここで、このエラーメッセージをどのようにフロントエンド側に表示するかで、方法が大きく2つあります。
1つ目はerror.js
を使う方法、2つ目はReact Hooks(useFormState
、あるいはuseActionState
)を使う方法です。
error.jsを使ったエラーハンドリング
error.js
を、app
フォルダ直下(layout.js
やpage.js
のある階層)に作り、下記のようなコードを書きます。
// error.js
"use client"
import { useEffect } from "react"
const Error = ({ error, reset }) => {
return (
<div>
<h2>{error.message}</h2>
<button onClick={() => reset()}>再試行</button>
</div>
)
}
export default Error
error.js
は、設置した階層以下で発生したエラーをキャッチする機能を持ちます。
注意してもらいたいのは、error.js
はクライアントコンポーネントにする必要があることです。"use client"
を忘れないようにしましょう。
次に、本記事冒頭のServer Actionsのcatch
内のコードを、エラー発生を通知する下記コードに変更します。
...
try{
await connectDB()
await UserModel.create(userData)
}catch{
throw new Error("エラー:ユーザー登録失敗") // 変更
}
redirect("/")
}
Error()
の中に書かれた言葉は、error.js
に渡されたerror
の中に入っているので、そこへアクセスをするために<h2>
タグの中でerror.message
と書いています。
なおerror.js
では、reset
という特別なfunctionが利用可能です。
以上がerror.js
のエラーハンドリングで、次に紹介するのがHooksを使った方法です。
Hooksを使ったエラーハンドリング
React Hooks、具体的にはuseFormState
を使います
Hooksを使うので、フロントエンド側のコンポーネントは、サーバーコンポーネントではなくクライアントコンポーネントにすることに注意しましょう。
"use client"
import { useFormState } from "react"
import { userRegister } from "@/app/actions/userRegister"
const initialState = {
message: "",
}
const Register = () => {
const [state, formAction] = useFormState(userRegister, initialState)
return (
<div>
<h1>ユーザー登録</h1>
<form action={formAction}>
<input type="text" name="name" placeholder="名前" required/>
<input type="text" name="email" placeholder="メールアドレス" required/>
<input type="text" name="password" placeholder="パスワード" required/>
<p>{state?.message}</p>
<button>登録</button>
</form>
</div>
)
}
export default Register
この方法では、Server Actionsのcatch
内ではError
を使う必要はなく、本記事冒頭で示したreturn
を使ったコードで大丈夫です。
"use server"
import { redirect } from "next/navigation"
import connectDB from "../utils/database"
import { UserModel } from "../utils/schemaModels"
export const userRegister = async (prevState, formData) => {
const userData = {
name: formData.get("name"),
email: formData.get("email"),
password: formData.get("password"),
}
try{
await connectDB()
await UserModel.create(userData)
}catch{
return {message: "エラー:ユーザー登録失敗"}
}
redirect("/")
}
ここで注目して欲しいのが、Server ActionsにformData
だけでなくprevState
も渡されていることです。
このようにすることで、フロントエンドのstate
の中に、catch
内に書かれたmessage
が渡されます。
なお、Reactバージョン19ではuseFormState
はuseActionState
となり、この場合は次のように書きます。
useActionState
では新たにisPending
が利用可能なので、<button>
タグの中に処理中であることを知らせるコードを追加しています。
"use client"
import { useActionState } from "react"
import { userRegister } from "@/app/actions/userRegister"
const initialState = {
message: "",
}
const Register = () => {
const [state, formAction, isPending] = useActionState(userRegister, initialState)
return (
<div>
<h1>ユーザー登録</h1>
<form action={formAction}>
<input type="text" name="name" placeholder="名前" required/>
<input type="text" name="email" placeholder="メールアドレス" required/>
<input type="text" name="password" placeholder="パスワード" required/>
<p>{state?.message}</p>
<button>{isPending ? "処理中..." : "登録"}</button>
</form>
</div>
)
}
export default Register
使い分け
本記事ではerror.js
とHooks(useFormState
、あるいはuseActionState
)を使う方法を紹介してきましたが、使い分けになにか指標はあるのでしょうか。
Next.js公式では、「起きることが予期できているエラー」にはHooksを、「起きることが予期できていないエラー」にはerror.js
を使う方法が紹介されています。
「起きることが予期できているエラー」とは、たとえばログイン機能を担うServer Actionsなどで、「ログインの前にユーザー登録をしてください」、「パスワードが間違っています」といった場合分けのエラー対応を行うケースです。
「throw new Error()
でエラーを発生させてerror.js
で表示する」というのは、より一般的で守備範囲の広い対応方法ということになります。
(*なお、Error()
の中にオブジェクトを含める方法や、throw
/throw Error()
/throw new Error()
の違いといったことは、こちらのstackoverflowのページを参考にしてください。)
お知らせ:新刊書リリース
2024年11月に、Server Actionsを使ってフルスタックアプリを開発する新刊書をリリースしました。
Next.js Server Actionsでつくるフルスタックアプリ
【2024年11月リリース。少ないコードですばやく本格的なフルスタックアプリを開発する方法を紹介】
880円 → 0円
Next.js/Reactビギナー向け入門です。
2024年11月時点で最新のNext.jsバージョン15を使用しています。
メルマガ配信中
(from 三好アキ/エンジニア)
React、Next.js、TypeScriptなど最新のウェブ開発のお役立ち情報を、ビギナー向けにかみ砕いて無料配信中。
(*配信はいつでも停止できます)