はじめに
Next.jsのApp Routerにおいて、Server ComponentsとClient Componentsが現れました。正確にはReactの機能ですが。そしてv14でServer Actionsが現れました。これも正確にはReactの機能ですが。これはServerなの、Clientなのなど使い分けが一見混乱するかと思います。実際に自分が使ってみてこうしたほうがわかりやすいと思い4つに分類してみました。
GETはServer Componentsで
Server Componentsの中にClient Componentsが来ます。GETの通信処理はServer Componentsで行いましょう。すると表示も早く、またuseEffectで非同期処理や、SWRなどの非同期処理ライブラリなどを使わずasync awaitだけでGETできます。ここでGETしたものをClientに渡せば、Client側はGETに関する処理を持たなくて済むので動きだけに専念できます。
このとき、Client ComponentsはSuspenseで囲んであげましょう。
import { Suspense } from "react"
import { MyClient } form "./mycomponent"
import { Loading } from "./loading" //ローディング
export default async function Index() {
const data = await fetch("https://api.example.com")
return (
<Suspense fallback={Loading}>
<MyClient data={data} />
</Suspsense>
)
}
POSTなどの送信処理はServer Actionsで
POSTなどの送信処理はServer Actionsを通して行いましょう。ORMを使ってDBに直接POSTをしていなくても、REST APIなどでPOSTしている場合もServer Actionsを通して行うことをおすすめします。ユーザーが送るデータに関することをすべてNext.js側で責任がもてます。サーバー側のバリデーション処理だったり。
Server Actions自体はForm APIを通しているのでPOSTしかできませんが、その先にAPIを通していればPATCHやPUT、DELETEも送れると思います。
また送信するAPIのurlもユーザーからは見えなくなるため、そしてわざわざNext.js側でAPIディレクトリを作らず済むのでServer Actionsを使うことをお勧めします。
UXにまつわる全てのことはClient Componentで
動的に表示する、画面にエラーメッセージを出す、条件によってコンポーネントの出しわけをする、ページ遷移をする等々、ユーザーの動きに関することはすべてClient Componentで処理しましょう。
そもそもuseStateなどのReact Hooksを使う場合はServer Componentsで使えません。
例えば、Server Actionsで送信に成功したさいにページ遷移するにはどうしたらいいでしょうか?Server ActionsでNextResponseを使ってredirectでページ遷移することはおすすめしません。なぜならServer Actionsは今現在はそこまで便利なAPIを提供されているわけでもなくほぼ純粋なNode.jsみたいなものなので、Server Actions自身でURLを取得したりパスを取得したりするのも複雑になります。
Client Componentsに持たせればuseRouterなどのNext.jsのライブラリを利用できるので、パスの取得やページ遷移、ページ遷移する前になんらかのClient処理などが行えます。
ここで、formはClient Componentsにおくことをお勧めします。React Hook Formなどのフォームライブラリも使えますし、フォーム送信前後の処理も書くことができます。
'use client'
import { useRouter } from 'next/navigation'
import { useForm } from 'react-hook-form'
import { Action } from './action' //自作Server Action
import { useAlert } from './AlertHooks' //自作Alert
export default function Form() {
const { trigger } = useForm()
const { alert } = useAlert()
const router = useRouter()
return (
<form
action={async (data) => {
//バリデーションの実行
await trigger()
await Action(data).then(() => {
alert('成功しました')
router.push('/dashboard')
})
}}
>
{/* ここにフォームの中身を書く */}
</form>
)
}
共通するサーバー処理はmiddlewareで
Next.jsにはmiddlewareというサーバー処理をするファイルがあります。例えば認証が必要なページでcookieがからだったらトップページにリダイレクトさせるなどの処理をするなど。ページ遷移はClientに持たせましょうといいましたが、認証処理などは逆にClient側でやるとuseEffectを駆使したり、一瞬ページが表示されてしまうなど工夫をしないとUXが悪くなるので、このようなリダイレクト処理はmiddlewareに持たせることをおすすめします。
終わりに
Next.jsの機能を大まかに4つにわけました。これによってApp routerの使い方に戸惑っている方も、うまく使い分けられるのではないでしょうか。
Server Components,Servr ActionsはGET POSTなどの通信処理に専念させると考えれば、Client Componentsが非常に書きやすくなると思います。