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?

More than 1 year has passed since last update.

【04】Next.js14でTodoアプリ作成(DELETE編)

Last updated at Posted at 2024-01-19

はじめに

Next.jsの知識を深めるために、Todoアプリを作ることにしました。そのメモを残しておきます。今回はデータ削除についてのメモを残します。何か間違っていることなどございましたらコメントいただけますと、とても喜びます。

今までの記事 環境構築編

https://qiita.com/naoyuki2/items/e974c630c6cbd3c55254

【01】Next.js14でTodoアプリ作成(CREATE編)

https://qiita.com/naoyuki2/items/7474d448de7769905d82

【02】Next.js14でTodoアプリ作成(SELECT編)

https://qiita.com/naoyuki2/items/2d8ad09f767f818e8860

【03】Next.js14でTodoアプリ作成(INSERT編)

https://qiita.com/naoyuki2/items/27d550ab3766a1869aaf

今回のゴール

データの削除ができるようになる。

image.png

1.APIの作成(DELETEメソッド)

DELETEメソッドの記述

今回は、ルートがsrc/app/api/todo/[id]/route.tsという場所にファイルを作成する。だが、見慣れない[id]というものが入っていると思う。これはダイナミックルーティングと呼ばれるNext.jsの機能。

例えば、http://localhost:3000/api/todo/123でもhttp://localhost:3000/api/todo/abcでも、同じルートにアクセスすることになる。

Next.js では [param] のようにしてページ名に角括弧(ブラケット)を使うことで動的なルーティング(別名 slug や pretty url など)を作成できます。

src/app/api/todo/[id]/route.ts
export async function DELETE(
    request: Request,
    { params }: { params: { id: string } },
) {
    // todoテーブルのレコードを削除
    const id = Number(params.id)
    try {
        const todo: todo = await prisma.todo.delete({
            where: {
                id: id,
            },
        })
        return NextResponse.json(todo)
    } catch (error) {
        return NextResponse.json(error)
    }
}

コードの解説

上の方のコード
export async function DELETE(
    request: Request,
    { params }: { params: { id: string } },
) {

今回の場合は、src/app/api/todo/[id]/route.tsというファイルであるため、[id]の部分が動的(値が一定ではない)なパラメータとなる。

そのパラメータを受け取るためにparamsというものを使用する。

[id]などのURLのパラメータは常に文字列として解釈される。そのため、型はstringとする。

const id = Number(params.id)

上でも述べたように、フロントエンドから送信されたidが数値であっても、APIルート内では文字列として受け取るため、Numberで数値型にキャストしている。

APIルートが[id]であるため、params.idで`アクセスできる。

const todo: todo = await prisma.todo.delete({
    where: {
        id: id,
    },
})

prismadelete関数を利用してレコードを削除する。どのレコードを削除するか指定するために、where句で条件を書く。今回は、paramsから取得したidtodoテーブルのidが一致するレコードを削除するというもの。SQLでいうDELETE FROM todo WHERE id = 1みたいなこと。

2.フロントエンドの実装

追加点は緑のところ。前回の記事からdeleteTodo関数、削除ボタンを追加した。

コードの全体

src/app/page.tsx
'use client'

import { useEffect, useState } from 'react'
import { todo } from '@prisma/client'

export default function Home() {
    const [todos, setTodos] = useState<todo[]>([])

    const [inputVal, setInputVal] = useState<string>('')

    useEffect(() => {
        const getTodo = async () => {
            const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/todo`)
            const data = await res.json()
            setTodos(data)
        }
        getTodo()
    }, [])

    const postTodo = async () => {
        if (!inputVal) return alert('タイトルを入力してください')

        const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/todo`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                title: inputVal,
            }),
        })
        const data = await res.json()
        setTodos([...todos, data])
        setInputVal('')
    }

+   const deleteTodo = async (id: number) => {
+       const res = await fetch(
+           `${process.env.NEXT_PUBLIC_API_URL}/todo/${id}`,
+           {
+               method: 'DELETE',
+           },
+       )
+       const data = await res.json()
+       setTodos(todos.filter((todo) => todo.id !== data.id))
+   }

    return (
        <>
            <input
                placeholder="タイトル入力"
                value={inputVal}
                onChange={(e) => setInputVal(e.target.value)}
            />

            <button onClick={() => postTodo()}>追加</button>

            {todos.map((todo) => (
                <div key={todo.id}>
                    <p>{todo.id}</p>
                    <p>{todo.title}</p>
                    <p>{todo.content}</p>
                    <p>{todo.isDone}</p>
                    <p>{new Date(todo.createdAt).toLocaleString()}</p>
                    <p>{new Date(todo.updatedAt).toLocaleString()}</p>
+                   <button
+                       onClick={() => deleteTodo(todo.id)}
+                       className="bg-rose-400 px-2 py-1 rounded"
+                   >
+                       削除
+                   </button>
                </div>
            ))}
        </>
    )
}

コードの解説

deleteTodo関数のコード
const deleteTodo = async (id: number) => {
    const res = await fetch(
        `${process.env.NEXT_PUBLIC_API_URL}/todo/${id}`,
        {
            method: 'DELETE',
        },
    )
    const data = await res.json()
    setTodos(todos.filter((todo) => todo.id !== data.id))
}

受け取ったidをもとにDELETEメソッドを呼び出す関数。

filter関数を使って、削除するレコード以外todosに格納している。

filterは配列の内容を指定した条件で絞り込む関数です。
配列の要素一つ一つを判定し、関数内でtrueが返ってきたもののみを抽出することができます。
また、この関数はArrayオブジェクトが持っている関数なので、配列に対してしか使用できません。

削除ボタンのとこ
<button
    onClick={() => deleteTodo(todo.id)}
    className="bg-rose-400 px-2 py-1 rounded"
>
    削除
</button>

onClickイベントで上で定義したdeleteTodoを発火させる。その際にtodo.idを渡す必要がある。

tailwind cssでスタイリングをしているが今回は触れないでおく。

3.削除してみよう

赤い削除ボタンをクリックして消すことができればゴール。

image.png

おわりに

次回のUPDATE処理難しそう

参考にさせていただいた記事

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?