0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【12-2】Next.js app routerのチュートリアルやってみる(React Server Actionsを使ったデータ更新処理)

Last updated at Posted at 2024-02-09

はじめに

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を使ったデータ作成処理)

https://qiita.com/naoyuki2/items/04ffef203ae798f8c7bc

第12-2章 データの変更

この章では以下を学びました。

  • データの更新(Update)

データの更新(Update)

データを更新するフォームは、データを作成するフォームと似ていますが、

データベース内のレコードを更新するために請求書のidを渡す必要がある点が異なります。

請求書を更新するには次の手順を実行します。

  1. 動的ルートセグメントを作成する
  2. idを取得する
  3. データベースから特定のデータを取得する
  4. データを更新する

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が渡されています。

/app/ui/invoices/table.tsx
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}

という形で渡します。

これで編集ページにアクセスできるようになりました。

/app/ui/invoices/buttons.tsx
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 />hrefidを渡したので、それをeditページ側で取得します。

params.ididを取得することができます。

/app/dashboard/invoices/[id]/edit/page.tsx
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 />コンポーネントに渡しましょう。

/dashboard/invoices/[id]/edit/page.tsx
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を渡して、発火させたいのですが、以下のように渡すことはできません。

なんで渡せないかはうまく説明できません。

/app/ui/invoices/edit-form.tsx
<form action={updateInvoice(id)}>

代わりに、JavaScriptbindを使用することができます。

/app/ui/invoices/edit-form.tsx
// ...
+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関数は、idformDataを受け取る関数です。

そのidの部分だけ先にbindを使って指定しているみたいです。

第1引数にnullを指定している理由はよく分からなかったので下のサイトをご覧ください。


次にactions.tsupdateInvoice関数を記述します。

/app/lib/actions.ts
// 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');
}

流れとしては、

  1. formDataからデータを抽出する
  2. Zodを使用して型を検証する
  3. 金額をセントに変換する
  4. 変数を SQL クエリに渡す
  5. revalidatePathでキャッシュをクリアし、再度サーバーにリクエストする
  6. redirectでユーザーを請求書のページにリダイレクトさせる

以上!

おわりに

次は削除です

次の記事

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?