データの更新
Next.jsでは、ReactのServer Functionsを使用してデータを更新できます。このページでは、Server Functionsを作成し呼び出す方法について説明します。
Server Functionsとは
Server Functionは、サーバー上で実行される非同期関数です。クライアントからネットワークリクエストを通じて呼び出すことができるため、非同期である必要があります。
actionまたは mutation コンテキストでは、Server Actionとも呼ばれます。
慣例により、Server ActionはstartTransitionで使用される非同期関数です。以下の場合に自動的に実行されます。
actionプロップを使用して<form>に渡される場合。formActionプロップを使用して<button>に渡される場合。
Next.jsでは、Server Actionsはフレームワークのキャッシングアーキテクチャと統合されます。アクションが呼び出されると、Next.jsは更新されたUIと新しいデータの両方を単一のサーバーラウンドトリップで返すことができます。
内部では、アクションはPOSTメソッドを使用し、このHTTPメソッドのみがそれらを呼び出すことができます。
Server Functionsの作成
Server Functionは、use serverディレクティブを使用して定義できます。非同期関数の最上部にディレクティブを配置して関数をServer Functionとしてマークするか、別ファイルの最上部に配置してそのファイルのすべてのエクスポートをマークできます。
export async function createPost(formData: FormData) {
'use server'
const title = formData.get('title')
const content = formData.get('content')
// データを更新
// キャッシュを再検証
}
export async function deletePost(formData: FormData) {
'use server'
const id = formData.get('id')
// データを更新
// キャッシュを再検証
}Server Components
Server Functionsは、関数本体の最上部に"use server"ディレクティブを追加して、Server Componentsにインライン化できます。
export default function Page() {
// Server Action
async function createPost(formData: FormData) {
'use server'
// ...
}
return <></>
}補足: Server Componentsはデフォルトで段階的な改善をサポートしており、JavaScriptが読み込まれていない場合や無効にされている場合でも、Server Actionsを呼び出すフォームが送信されます。
Client Components
Client Componentsでは Server Functionsを定義することはできません。ただし、最上部に"use server"ディレクティブを持つファイルからそれらをインポートして、Client Componentsで呼び出すことができます。
'use server'
export async function createPost() {}'use client'
import { createPost } from '@/app/actions'
export function Button() {
return <button formAction={createPost}>Create</button>
}補足: Client Componentsでは、Server Actionsを呼び出すフォームは、JavaScriptが読み込まれていない場合は送信をキューイングし、ハイドレーションを優先します。ハイドレーション後、ブラウザはフォーム送信時にリフレッシュされません。
アクションをプロップとして渡す
アクションをプロップとして Client Componentに渡すこともできます。
<ClientComponent updateItemAction={updateItem} />'use client'
export default function ClientComponent({
updateItemAction,
}: {
updateItemAction: (formData: FormData) => void
}) {
return <form action={updateItemAction}>{/* ... */}</form>
}Server Functionsの呼び出し
Server Functionを呼び出す主な方法は2つあります。
補足: Server Functionsはサーバー側の変更用に設計されています。現在、クライアントはそれらを1つずつディスパッチして待機します。これは実装の詳細であり、変更される可能性があります。並列データフェッチが必要な場合は、Server Componentsでデータフェッチを使用するか、単一のServer Function内またはRoute Handler内で並列の作業を実行してください。
フォーム
ReactはHTML<form>要素を拡張して、HTMLのactionプロップでServer Functionを呼び出せるようにします。
フォーム内で呼び出された場合、関数は自動的にFormDataオブジェクトを受け取ります。ネイティブのFormDataメソッドを使用してデータを抽出できます。
import { createPost } from '@/app/actions'
export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<input type="text" name="content" />
<button type="submit">Create</button>
</form>
)
}'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title')
const content = formData.get('content')
// データを更新
// キャッシュを再検証
}イベントハンドラー
Client Componentでは、onClickなどのイベントハンドラーを使用してServer Functionを呼び出すことができます。
'use client'
import { incrementLike } from './actions'
import { useState } from 'react'
export default function LikeButton({ initialLikes }: { initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes)
return (
<>
<p>Total Likes: {likes}</p>
<button
onClick={async () => {
const updatedLikes = await incrementLike()
setLikes(updatedLikes)
}}
>
Like
</button>
</>
)
}例
保留中の状態を表示する
Server Functionの実行中に、ReactのuseActionStateフックでローディングインジケーターを表示できます。このフックはpendingブール値を返します。
'use client'
import { useActionState, startTransition } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'
export function Button() {
const [state, action, pending] = useActionState(createPost, false)
return (
<button onClick={() => startTransition(action)}>
{pending ? <LoadingSpinner /> : 'Create Post'}
</button>
)
}再検証
更新を実行した後、Server Function内でrevalidatePathまたはrevalidateTagを呼び出して、Next.jsキャッシュを再検証し、更新されたデータを表示できます。
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
'use server'
// データを更新
// ...
revalidatePath('/posts')
}リダイレクト
更新を実行した後、ユーザーを別のページにリダイレクトすることができます。Server Function内でredirectを呼び出すことでこれを実現できます。
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// データを更新
// ...
revalidatePath('/posts')
redirect('/posts')
}redirectの呼び出しはフレームワークが処理する制御フロー例外をスローします。その後のコードは実行されません。新しいデータが必要な場合は、事前にrevalidatePathまたはrevalidateTagを呼び出してください。
Cookie
Server Action内でcookiesAPIを使用して、cookieのget、set、deleteができます。
Server Action内でcookieを設定または削除すると、Next.jsはサーバー上で現在のページとそのレイアウトを再レンダリングするため、UIは新しいcookie値を反映します。
補足: サーバーの更新は現在のReactツリーに適用され、必要に応じてコンポーネントを再レンダリング、マウント、またはアンマウントします。再レンダリングされたコンポーネントのクライアント状態は保持され、依存関係が変更された場合は効果が再実行されます。
'use server'
import { cookies } from 'next/headers'
export async function exampleAction() {
const cookieStore = await cookies()
// cookieを取得
cookieStore.get('name')?.value
// cookieを設定
cookieStore.set('name', 'Delba')
// cookieを削除
cookieStore.delete('name')
}useEffect
ReactのuseEffectフックを使用して、コンポーネントがマウントされるか依存関係が変更されるときにServer Functionを呼び出すことができます。これは、グローバルイベントに依存する変更や、自動的にトリガーされる必要がある変更に役立ちます。たとえば、アプリショートカット用のonKeyDown、無限スクロール用のintersection observerフック、またはビュー数を更新するためのコンポーネントマウント時など。
'use client'
import { incrementViews } from './actions'
import { useState, useEffect, useTransition } from 'react'
export default function ViewCount({ initialViews }: { initialViews: number }) {
const [views, setViews] = useState(initialViews)
const [isPending, startTransition] = useTransition()
useEffect(() => {
startTransition(async () => {
const updatedViews = await incrementViews()
setViews(updatedViews)
})
}, [])
// `isPending`を使用してユーザーにフィードバックを提供できます
return <p>Total Views: {views}</p>
}