1
1

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.

【05】Next.js14でTodoアプリ作成(UPDATE編)

Last updated at Posted at 2024-01-21

はじめに

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

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

https://qiita.com/naoyuki2/items/8fcb8ae5583a1aef8a2d

今回のゴール

データの更新ができるようになる。とはいっても、完了、未完了の値(isDone)のみ更新できるようにする。

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

PATCHメソッドの記述

src/app/api/todo/[id]/route.tsPATCHメソッドを記述していく。DELETEメソッドがすでにあるのでその下に書く。

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

コードの解説

前回の記事で解説したところは省略する。

const todo: todo = await prisma.todo.update({
    where: {
        id: id,
    },
    data: {
        isDone: body.isDone,
    },
})
return NextResponse.json(todo)

今回はprismaupdate関数を使う。

whereには前回と同様にリクエストパラメータから取得したidを使用する。

今回はisDoneのみ更新するためdataに変更するカラム名(isDone)と値を記述する。

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

前回の記事からpatchTodo関数とチェックボックスを追加した。

コードの全体

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))
    }

+   const patchTodo = async (todo: todo) => {
+       const res = await fetch(
+           `${process.env.NEXT_PUBLIC_API_URL}/todo/${todo.id}`,
+           {
+               method: 'PATCH',
+               headers: {
+                   'Content-Type': 'application/json',
+               },
+               body: JSON.stringify({
+                   isDone: !todo.isDone,
+               }),
+           },
+       )
+       const data = await res.json()
+       setTodos(
+           todos.map((todo) => {
+               if (todo.id === data.id) {
+                   return data
+               }
+               return todo
+           }),
+       )
+   }

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

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

            {todos.map((todo) => (
                <div key={todo.id}>
+                   <input
+                       type="checkbox"
+                       checked={todo.isDone}
+                       onChange={() => patchTodo(todo)}
+                   />
                    <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>
            ))}
        </>
    )
}

コードの解説

src/app/page.tsxのpatchTodo関数
const patchTodo = async (todo: todo) => {
    const res = await fetch(
        `${process.env.NEXT_PUBLIC_API_URL}/todo/${todo.id}`,
        {
            method: 'PATCH',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                isDone: !todo.isDone,
            }),
        },
    )

bodyにはisDoneを反転した(!をつけると反転)値を渡す。falseの状態でチェックボックスをクリックしたらtrueに、逆にtrueの状態でクリックしたらfalseに。

    const data = await res.json()
    setTodos(
        todos.map((todo) => {
            if (todo.id === data.id) {
                return data
            }
            return todo
        }),
    )
}

現在保存されているtodomap関数で一つずつ取り出す。

取り出したtodoidと、変更したdataidが一致していれば、受け取ったdataの方を返す。

一致していない場合は今まで通りのtodoを返す。

3.更新してみよう

チェックボックスをクリックして変更されれば成功。

image.png

image.png

おわりに

これでCRUD処理は全て実装完了!でも他にも色々つけ足してみよう。

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

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?