データの更新
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リファレンス
役に立ちましたか?