データを変更する方法
Next.jsでは、ReactのServer Functionsを使用してデータを変更できます。このページでは、Server Functionsを作成し、呼び出す方法について説明します。
Server Functionsの作成
Server Functionは、use server
ディレクティブを使用して定義できます。このディレクティブを非同期関数の先頭に置くことで、その関数をServer Functionとしてマークするか、別のファイルの先頭に置いてそのファイルのすべてのエクスポートをマークできます。ほとんどの場合、別のファイルを使用することをお勧めします。
- TypeScript
- JavaScript
'use server'
export async function createPost(formData: FormData) {}
export async function deletePost(formData: FormData) {}
'use server'
export async function createPost(formData) {}
export async function deletePost(formData) {}
Server Components
Server Functionsは、関数本体の先頭に"use server"
ディレクティブを追加することで、Server Componentsにインライン化できます。
- TypeScript
- JavaScript
export default function Page() {
// Server Action
async function createPost() {
'use server'
// データを変更
// ...
return <></>
}
export default function Page() {
// Server Action
async function createPost() {
'use server'
// データを変更
// ...
}
return <></>
}
Client Components
Client ComponentsでServer Functionsを定義することはできません。ただし、"use server"
ディレクティブが先頭にあるファイルからインポートすることで、Client Componentsで呼び出すことができます。
- TypeScript
- JavaScript
'use server'
export async function createPost() {}
'use server'
export async function createPost() {}
- TypeScript
- JavaScript
'use client'
import { createPost } from '@/app/actions'
export function Button() {
return <button formAction={createPost}>Create</button>
}
'use client'
import { createPost } from '@/app/actions'
export function Button() {
return <button formAction={createPost}>Create</button>
}
Server Functionsの呼び出し
Server Functionを呼び出す主な方法は2つあります。
フォーム
ReactはHTMLの<form>
要素を拡張して、Server FunctionをHTMLのaction
プロップで呼び出せるようにします。
フォームで呼び出されると、関数は自動的にFormData
オブジェクトを受け取ります。ネイティブのFormData
メソッドを使用してデータを抽出できます。
- TypeScript
- JavaScript
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>
)
}
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>
)
}
- TypeScript
- JavaScript
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title')
const content = formData.get('content')
// データを変更
// キャッシュを再検証
}
'use server'
export async function createPost(formData) {
const title = formData.get('title')
const content = formData.get('content')
// データを変更
// キャッシュを再検証
}
Good to know:
action
プロップに渡されると、Server FunctionsはServer Actionsとも呼ばれます。
イベントハンドラ
Client ComponentでonClick
などのイベントハンドラを使用してServer Functionを呼び出すことができます。
- TypeScript
- JavaScript
'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>
</>
)
}
'use client'
import { incrementLike } from './actions'
import { useState } from 'react'
export default function LikeButton({ initialLikes }) {
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
ブール値を返します。
- TypeScript
- JavaScript
'use client'
import { useActionState } 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={async () => action()}>
{pending ? <LoadingSpinner /> : 'Create Post'}
</button>
)
}
'use client'
import { useActionState } 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={async () => action()}>
{pending ? <LoadingSpinner /> : 'Create Post'}
</button>
)
}
キャッシュの再検証
変更を行った後、Server Function内でrevalidatePath
またはrevalidateTag
を呼び出すことで、Next.jsのキャッシュを再検証し、更新されたデータを表示できます。
- TypeScript
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
// データを変更
// ...
revalidatePath('/posts')
}
- JavaScript
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData) {
// データを変更
// ...
revalidatePath('/posts')
}
リダイレクト
変更を行った後、ユーザーを別のページにリダイレクトしたい場合があります。Server Function内でredirect
を呼び出すことでこれを行うことができます。
- TypeScript
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// データを変更
// ...
redirect('/posts')
}
- JavaScript
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData) {
// データを変更
// ...
redirect('/posts')
}