fetchを使ったAPI通信(Next.js)その③

Next.js-3.API通信編-

データ追加(Create)

app/components/CreatDog.js

"use client";
import { useState } from "react";

export default function CreateDog(props) {
    const [newDog, setNewDog] = useState({ name: "", age: 0 });

    const addDog = async () => {
        const newEntry = { name: newDog.name, age: newDog.age };

        const response = await fetch("http://localhost:3001/dogs", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(newEntry)
        });

        if (response.ok) {
            const addedDog = await response.json(); // `json-server` が追加したデータを返す
            props.setDogs([...props.dogs, addedDog]);
            setNewDog({ name: "", age: 0 });
        }
    };

    return (
        <div>
            <h2>🐕 新しい犬を追加</h2>
            <input
                type="text"
                placeholder="名前"
                value={newDog.name}
                onChange={(e) => setNewDog({ ...newDog, name: e.target.value })}
            />
            <input
                type="number"
                placeholder="年齢"
                value={newDog.age}
                onChange={(e) => setNewDog({ ...newDog, age: Number(e.target.value) })}
            />
            <button onClick={addDog}>追加</button>
        </div>
    );
}

コード解説

const [newDog, setNewDog] = useState({ name: "", age: 0 });

入力フォーム用のstateを設定します。

addDog(API通信)処理

「追加」ボタンをクリックしたときのイベントハンドラー「addDog」によるAPI通信の処理を見ていきます。

 const addDog = async () => {
        const newEntry = { name: newDog.name, age: newDog.age };

        const response = await fetch("http://localhost:3001/dogs", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(newEntry)
        });

        if (response.ok) {
            const addedDog = await response.json(); 
            props.setDogs([...props.dogs, addedDog]);
            setNewDog({ name: "", age: 0 });
        }
    };

1行ずつ説明していきます。

const addDog = async () => {

これは関数の定義
👉 const addDog関数を変数 addDog に代入している
👉 async () =>「この関数は非同期処理を行う」 という宣言

💡 「非同期処理って何?」
サーバーと通信するとき、データが返ってくるまで待つ必要がある
その間に他の処理を進められるのが「非同期」
この関数は await を使うために async をつける

const newEntry = { name: newDog.name, age: newDog.age };

新しい犬のデータをオブジェクトで作る
👉 {}オブジェクト(データのまとまり)を作る記号
👉 name: newDog.name は、newDog.name の値を name というキーに入れる
👉 age: newDog.age も同じ

💡 newDog.name って何?」
これは React の useState で管理されている newDogname の値
つまり newDog に入力したnameとageを newEntry に入れてる

const response = await fetch("http://localhost:3001/dogs", {

サーバー(json-server)にデータを送る
👉 fetch("URL")指定した URL にデータを送ったり受け取ったりする関数
👉 await をつけることで、データが返ってくるまで待つ

💡 await をつけないとどうなる?」
データを待たずに次の処理が進んで、値が undefined になったりバグる

 method: "POST",

POST メソッドを使う
👉 HTTP には 4つの基本的なメソッド(動作)がある

メソッド何をする?
GETデータを取得(例:犬の一覧をもらう)
POSTデータを追加(例:新しい犬を登録する)
PUTデータを更新(例:犬の年齢を変更する)
DELETEデータを削除(例:犬をリストから消す)

💡 POST だから、新しい犬のデータを送る処理になる

headers: { "Content-Type": "application/json" },

「このデータは JSON 形式だよ!」とサーバーに伝える
👉 headers は、サーバーに送るデータの種類を指定する部分
👉 "Content-Type": "application/json"「送るデータは JSON 形式」と宣言する

body: JSON.stringify(newEntry)

データを JSON に変換して送る
👉 JSON.stringify(データ) は、JavaScript のオブジェクトを JSON 文字列に変換する関数

if (response.ok) {

サーバーが「成功!」と返したら実行
👉 response.ok は、通信が成功 (200 番台のステータス) だったら true になる
👉 もし false だったら、何かエラーが起きたということ

💡 response.ok ってどういう意味?
サーバーが「リクエスト成功!」と返したら true になる
false の場合はエラー(例:404 Not Found500 Server Error

const addedDog = await response.json();

サーバーから返ってきたデータを JSON に変換
👉 response.json() は、サーバーのレスポンスを JSON 形式のデータに変換する関数
👉 await をつけることで、データが変換されるまで待つ

💡 「なぜ response.json() が必要?」
サーバーから返ってくるデータは JSON 形式の文字列だから、そのままじゃ使えない
だから response.json() で JavaScript のオブジェクトに変換する

props.setDogs([...props.dogs, addedDog]);

React の state を更新して、新しい犬を追加
👉 props.setDogs() は、親コンポーネントの dogs を更新する関数
👉 [...props.dogs, addedDog] は、現在の dogs 配列に addedDog を追加する

💡 なぜ ...props.dogs (スプレッド構文)を使うの?
React の stateprops.dogs)は、直接変更できない
だから、新しい配列 [...] を作って、更新する

setNewDog({ name: "", age: 0 });

入力フォームをリセット!
👉 setNewDog({ name: "", age: 0 }) は、新しい犬の情報を 空にする!
👉 次に入力するときに、前回のデータが残らないようにする!

💡 setNewDog とは?」
useState で管理している newDog を更新する関数!
フォームの中身をリセットするために使う!

🐵 addDog の処理の流れ

1️⃣ newEntry を作る(新しい犬の情報)
2️⃣ fetch でサーバーにデータを送る(POST リクエスト)
3️⃣ サーバーが処理して response を返す
4️⃣ response.ok なら、データを JSON に変換
5️⃣ props.setDogs([...props.dogs, addedDog])state を更新
6️⃣ setNewDog({ name: "", age: 0 }) でフォームをリセット

入力フォームで入力された情報をサーバーに送ってデータベースを更新し、通信がOKであれば、ページで表示されている一覧の内容も更新するという流れの処理です

responseでサーバーから返ってくるデータの内容は実際はバックエンド側のアプリでPOSTに対して何をreturnするかの記述に依存します。今回は、

👉 json-serverPOST リクエストを送ると、デフォルトで 送られたデータに id を追加して返す仕様に準じています。

JSX

return (
        <div>
            <h2>🐕 新しい犬を追加</h2>
            <input
                type="text"
                placeholder="名前"
                value={newDog.name}
                onChange={(e) => setNewDog({ ...newDog, name: e.target.value })}
            />
            <input
                type="number"
                placeholder="年齢"
                value={newDog.age}
                onChange={(e) => setNewDog({ ...newDog, age: Number(e.target.value) })}
            />
            <button onClick={addDog}>追加</button>
        </div>
    );

inputタグの入力フォームでそれぞれnewDogのnameとageをsetNewDog更新用関数を使って更新しています。ここでも、stateの値そのものを変更はできないので、スプレッド構文を使ってそれぞれのキーの値を更新する記述をしています。

onChange={(e) => setNewDog({ ...newDog, name: e.target.value })}
onChange={(e) => setNewDog({ ...newDog, age: Number(e.target.value) })}

「age」を更新するinputタグでは念の為Number()を使って入力された値を数値に変換しています。

全体体に少し複雑に見えますが、すべてを理解するというよりは「書き方を覚える」というスタンスでOKです。

コメント

タイトルとURLをコピーしました