データの更新
Next.jsでは、Reactのサーバー関数を使用してデータを更新できます。このページでは、サーバー関数の作成方法と呼び出し方法を説明します。
サーバー関数とは?
サーバー関数とは、サーバー上で実行される非同期関数です。クライアントからネットワークリクエストを通じて呼び出すことができるため、非同期である必要があります。
actionまたはミューテーションのコンテキストでは、それらはサーバーアクションとも呼ばれます。
慣習として、サーバーアクションはstartTransitionと共に使用される非同期関数です。この関数が
actionpropを使用して<form>に渡された場合。formActionpropを使用して<button>に渡された場合。
Next.jsでは、サーバーアクションはフレームワークのキャッシュアーキテクチャと統合されています。アクションが呼び出されると、Next.jsは更新されたUIと新しいデータを単一のサーバーラウンドトリップで返すことができます。
内部的には、アクションはPOSTメソッドを使用しており、このHTTPメソッドのみがそれらを呼び出すことができます。
サーバー関数の作成
サーバー関数は、use serverディレクティブを使用して定義できます。ディレクティブを非同期関数の先頭に配置して関数をサーバー関数としてマークするか、個別のファイルの先頭に配置してそのファイルのエクスポートすべてをマークできます。
export async function createPost(formData: FormData) {
'use server'
const title = formData.get('title')
const content = formData.get('content')
// Update data
// Revalidate cache
}
export async function deletePost(formData: FormData) {
'use server'
const id = formData.get('id')
// Update data
// Revalidate cache
}サーバーコンポーネント
サーバー関数は、関数本体の先頭に"use server"ディレクティブを追加することで、サーバーコンポーネントにインライン化できます。
export default function Page() {
// Server Action
async function createPost(formData: FormData) {
'use server'
// ...
}
return <></>
}参考情報:サーバーコンポーネントはデフォルトでプログレッシブエンハンスメントをサポートしています。つまり、JavaScriptがまだロードされていないか無効になっている場合でも、サーバーアクションを呼び出すフォームは送信されます。
クライアントコンポーネント
クライアントコンポーネントでサーバー関数を定義することはできません。ただし、"use server"ディレクティブが先頭に付いたファイルからインポートすることで、クライアントコンポーネントで呼び出すことができます。
'use server'
export async function createPost() {}'use client'
import { createPost } from '@/app/actions'
export function Button() {
return <button formAction={createPost}>Create</button>
}参考情報:クライアントコンポーネントでは、JavaScriptがまだロードされていない場合、サーバーアクションを呼び出すフォームはサブミッションをキューイングし、ハイドレーションが優先されます。ハイドレーション後、フォーム送信時にブラウザはリフレッシュされません。
アクションをpropsとして渡す
アクションをクライアントコンポーネントにpropとして渡すこともできます。
<ClientComponent updateItemAction={updateItem} />'use client'
export default function ClientComponent({
updateItemAction,
}: {
updateItemAction: (formData: FormData) => void
}) {
return <form action={updateItemAction}>{/* ... */}</form>
}サーバー関数の呼び出し
サーバー関数を呼び出す主な方法が2つあります。
参考情報:サーバー関数はサーバーサイドのミューテーションのために設計されています。クライアントは現在、それらを1つずつディスパッチして待機します。これは実装上の詳細であり、変更される可能性があります。並列データ取得が必要な場合は、サーバーコンポーネントでデータ取得を使用するか、単一のサーバー関数またはルートハンドラー内で並列処理を実行してください。
フォーム
ReactはHTMLの<form>要素を拡張し、HTMLのaction propを使用してサーバー関数を呼び出すことができるようにします。
フォームで呼び出されると、関数は自動的に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')
// Update data
// Revalidate cache
}イベントハンドラー
onClickなどのイベントハンドラーを使用して、クライアントコンポーネントでサーバー関数を呼び出すことができます。
'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>
</>
)
}例
保留中の状態を表示する
サーバー関数の実行中は、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>
)
}再検証
更新を実行した後、サーバー関数内でrevalidatePathまたはrevalidateTagを呼び出すことで、Next.jsキャッシュを再検証し、更新されたデータを表示できます。
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
'use server'
// Update data
// ...
revalidatePath('/posts')
}リダイレクト
更新を実行した後、ユーザーを別のページにリダイレクトしたい場合があります。これは、サーバー関数内でredirectを呼び出すことで実現できます。
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// Update data
// ...
revalidatePath('/posts')
redirect('/posts')
}redirectの呼び出しは、フレームワークが処理する制御フロー例外をスローします。それ以降のコードは実行されません。新しいデータが必要な場合は、事前にrevalidatePathまたはrevalidateTagを呼び出してください。
Cookie
サーバーアクション内でcookies APIを使用して、Cookieをget、set、deleteできます。
サーバーアクションでCookieを設定または削除すると、Next.jsはサーバー上で現在のページとそのレイアウトを再レンダリングするため、UIは新しいCookie値を反映します。
参考情報:サーバー更新は現在のReactツリーに適用され、必要に応じてコンポーネントの再レンダリング、マウント、またはアンマウントが行われます。クライアント状態は再レンダリングされたコンポーネント用に保持され、依存関係が変更された場合はエフェクトが再実行されます。
'use server'
import { cookies } from 'next/headers'
export async function exampleAction() {
const cookieStore = await cookies()
// Get cookie
cookieStore.get('name')?.value
// Set cookie
cookieStore.set('name', 'Delba')
// Delete cookie
cookieStore.delete('name')
}useEffect
コンポーネントのマウント時または依存関係が変更されたときにサーバーアクションを呼び出すために、ReactのuseEffectフックを使用できます。これは、グローバルイベントに依存するミューテーションや、自動的にトリガーする必要があるミューテーションに役立ちます。例えば、アプリのショートカット用の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)
})
}, [])
// You can use `isPending` to give users feedback
return <p>Total Views: {views}</p>
}APIリファレンス
役に立ちましたか?