はじめに
Next.js app routerのチュートリアルの第12-2章のアウトプットをします。
前の記事
【01】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/af58da3d20cbc790e767
【02】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/edf450b3ee135e83d1e8
【03】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/612221eac233aa9cbb74
【04】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/62f9beccbfe36eaf7f90
【05】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/8b71b1d1df7c9435a9c9
【06】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/58130c3cfbaf8a573de2
【07】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/2c2da0f8071e60454679
【08】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/45f45fcb9cc14506f79f
【09】Next.js app routerのチュートリアルやってみる(loading.tsxとSuspenseでストリーミング)
https://qiita.com/naoyuki2/items/717694288ec6017a3af2
【10】Next.js app routerのチュートリアルやってみる(部分的な事前レンダリング)
https://qiita.com/naoyuki2/items/8062f755b0679fe925b1
【11-1】Next.js app routerのチュートリアルやってみる(URLパラメーターを利用した検索機能)
https://qiita.com/naoyuki2/items/2be9503ac80fc4a1fa6a
【11-2】Next.js app routerのチュートリアルやってみる(URL パラメータを利用したページネーション)
https://qiita.com/naoyuki2/items/fd00dc2b376e7d87fb44
【12-1】Next.js app routerのチュートリアルやってみる(React Server Actionsを使ったデータ作成処理)
第12-2章 データの変更
この章では以下を学びました。
- データの更新(Update)
データの更新(Update)
データを更新するフォームは、データを作成するフォームと似ていますが、
データベース内のレコードを更新するために請求書のid
を渡す必要がある点が異なります。
請求書を更新するには次の手順を実行します。
- 動的ルートセグメントを作成する
-
id
を取得する - データベースから特定のデータを取得する
- データを更新する
1. 動的ルートセグメントを作成する
Next.jsでは、データに基づいてルートを作成したい場合に、動的ルートセグメントを作成できます。
フォルダー名を角括弧[]で囲むことによって、動的ルート セグメントを作成できます。
フォルダー内に/invoices/[id]/edit/page.tsx
という新しい動的ルート作成しましょう。
ファイル構造は次のようになります。
.
└── invoices/
├── [id]/
│ └── edit/
│ └── page.tsx
├── create/
│ └── page.tsx
└── page.tsx
上の構成にして、edit
配下のpage.tsx
にアクセスしたい場合は、
/invoices/1/edit
でアクセスできます。
1
の部分は何でもいいです。
以下は<Table />
コンポーネントの一部です。
この中で<UpdateInvoice />
コンポーネントにid
が渡されています。
export default async function InvoicesTable({
query,
currentPage,
}: {
query: string;
currentPage: number;
}) {
return (
// ...
<td className="flex justify-end gap-2 whitespace-nowrap px-6 py-4 text-sm">
+ <UpdateInvoice id={invoice.id} />
<DeleteInvoice id={invoice.id} />
</td>
// ...
);
}
その<UpdateInvoice />
コンポーネント内では、id
を受けとるようにします。
受けとったid
をNext.jsが提供している<Link />
のhref
に
{/dashboard/invoices/${id}/edit
}
という形で渡します。
これで編集ページにアクセスできるようになりました。
import { PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
// ...
export function UpdateInvoice({ id }: { id: string }) {
return (
<Link
href={`/dashboard/invoices/${id}/edit`}
className="rounded-md border p-2 hover:bg-gray-100"
>
<PencilIcon className="w-5" />
</Link>
);
}
2. idを取得する
先ほど<Link />
のhref
にid
を渡したので、それをedit
ページ側で取得します。
params.id
でid
を取得することができます。
import Form from '@/app/ui/invoices/edit-form';
import Breadcrumbs from '@/app/ui/invoices/breadcrumbs';
+export default async function Page({ params }: { params: { id: string } }) {
+ const id = params.id;
return (
<main>
<Breadcrumbs
breadcrumbs={[
{ label: 'Invoices', href: '/dashboard/invoices' },
{
label: 'Edit Invoice',
href: `/dashboard/invoices/${id}/edit`,
active: true,
},
]}
/>
<Form />
</main>
);
}
3. データベースから特定のデータを取得する
データを更新するためには元のデータの情報が必要になります。
先ほど取得したid
を使ってデータを取得し<Form />
コンポーネントに渡しましょう。
import Form from '@/app/ui/invoices/edit-form';
import Breadcrumbs from '@/app/ui/invoices/breadcrumbs';
+import { fetchInvoiceById, fetchCustomers } from '@/app/lib/data';
export default async function Page({ params }: { params: { id: string } }) {
const id = params.id;
+ const [invoice, customers] = await Promise.all([
+ fetchInvoiceById(id),
+ fetchCustomers(),
+ ]);
// ...
}
これで事前に内容が入力されているフォームが完成しました。
4. データを更新する
最後に、フォームのupdateInvoice
関数にid
を渡して、発火させたいのですが、以下のように渡すことはできません。
なんで渡せないかはうまく説明できません。
<form action={updateInvoice(id)}>
代わりに、JavaScript
のbind
を使用することができます。
// ...
+import { updateInvoice } from '@/app/lib/actions';
export default function EditInvoiceForm({
invoice,
customers,
}: {
invoice: InvoiceForm;
customers: CustomerField[];
}) {
+ const updateInvoiceWithId = updateInvoice.bind(null, invoice.id);
return (
+ <form action={updateInvoiceWithId}>
<input type="hidden" name="id" value={invoice.id} />
</form>
);
}
updateInvoice
関数は、id
とformData
を受け取る関数です。
そのid
の部分だけ先にbind
を使って指定しているみたいです。
第1引数にnull
を指定している理由はよく分からなかったので下のサイトをご覧ください。
次にactions.ts
にupdateInvoice
関数を記述します。
// Use Zod to update the expected types
const UpdateInvoice = FormSchema.omit({ id: true, date: true });
// ...
export async function updateInvoice(id: string, formData: FormData) {
const { customerId, amount, status } = UpdateInvoice.parse({
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
});
const amountInCents = amount * 100;
await sql`
UPDATE invoices
SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status}
WHERE id = ${id}
`;
revalidatePath('/dashboard/invoices');
redirect('/dashboard/invoices');
}
流れとしては、
-
formData
からデータを抽出する -
Zod
を使用して型を検証する - 金額をセントに変換する
- 変数を SQL クエリに渡す
-
revalidatePath
でキャッシュをクリアし、再度サーバーにリクエストする -
redirect
でユーザーを請求書のページにリダイレクトさせる
以上!
おわりに
次は削除です
次の記事