はじめに
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編)
今回のゴール
データの削除ができるようになる。
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 など)を作成できます。
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,
},
})
prisma
のdelete
関数を利用してレコードを削除する。どのレコードを削除するか指定するために、where
句で条件を書く。今回は、params
から取得したid
とtodo
テーブルのid
が一致するレコードを削除するというもの。SQL
でいうDELETE FROM todo WHERE id = 1
みたいなこと。
2.フロントエンドの実装
追加点は緑のところ。前回の記事からdeleteTodo
関数、削除ボタンを追加した。
コードの全体
'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>
))}
</>
)
}
コードの解説
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.削除してみよう
赤い削除ボタンをクリックして消すことができればゴール。
おわりに
次回のUPDATE
処理難しそう
参考にさせていただいた記事