データ更新(Update)
app/components/UpdateDog.js
"use client";
import { useState } from "react";
export default function UpdateDog(props) {
const [updatedValues, setUpdatedValues] = useState({}); // 名前&年齢の変更用ステート
// ✅ 名前・年齢の入力変更を管理する関数
const handleInputChange = (id, field, value) => {
setUpdatedValues({
...updatedValues,
[id]: { ...updatedValues[id], [field]: value }
});
};
// ✅ 名前&年齢をまとめて更新
const updateDogInfo = async (id) => {
const currentDog = props.dogs.find((dog) => dog.id === id);
const newData = {
name: updatedValues[id]?.name ?? currentDog.name, // 入力がなければ元の値
age: updatedValues[id]?.age ? Number(updatedValues[id].age) : currentDog.age // 入力がなければ元の値
};
const response = await fetch(`http://localhost:3001/dogs/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData)
});
if (response.ok) {
props.setDogs(
props.dogs.map((dog) => (dog.id === id ? { ...dog, ...newData } : dog))
);
}
};
return (
<div>
<h2>🐕 犬の情報を更新</h2>
{props.dogs.map((dog) => (
<div key={dog.id}>
{/* 名前の入力欄 */}
<input
type="text"
value={updatedValues[dog.id]?.name ?? dog.name}
onChange={(e) => handleInputChange(dog.id, "name", e.target.value)}
/>
{/* 年齢の入力欄 */}
<input
type="number"
value={updatedValues[dog.id]?.age ?? dog.age}
onChange={(e) => handleInputChange(dog.id, "age", e.target.value)}
/>
{/* 更新ボタン(1回の通信で名前&年齢を変更) */}
<button onClick={() => updateDogInfo(dog.id)}>更新</button>
</div>
))}
</div>
);
}

コード解説
state設定
const [updatedValues, setUpdatedValues] = useState({});
✅ 犬の名前と年齢の変更を一時的に保存するための状態管理のためのstate
実際の updatedValues
の中身(例)
{
"1": { "name": "ゴールデンレトリバー", "age": 5 },
"2": { "name": "柴犬", "age": 3 }
}
📌 このように id
をキーとして、name
と age
を管理
handleInputChange(名前・年齢の入力変更を管理する関数)
const handleInputChange = (id, field, value) => {
setUpdatedValues({
...updatedValues,
[id]: { ...updatedValues[id], [field]: value }
});
};
✅ ユーザーが入力した値を updatedValues
に保存
✅ スプレッド構文 ...updatedValues
で、元の updatedValues
の内容を維持
✅ 新しい id
のデータを { ...updatedValues[id], [field]: value }
でスプレッド構文を使って更新
updateDogInfo(API通信する関数)
const updateDogInfo = async (id) => {
「id」を引数とする「更新」ボタンをクリックしたときのイベントハンドラでAPI通信します。
currentDog に id に一致する犬の情報を取得
const currentDog = props.dogs.find((dog) => dog.id === id);
find関数の説明

親コンポーネントから渡ってきたprops.dogsの中で「id」が合致する犬の要素を抽出しています。
更新するデータ newData を作成
const newData = {
name: updatedValues[id]?.name ?? currentDog.name,
age: updatedValues[id]?.age ? Number(updatedValues[id].age) : currentDog.age
};
name: updatedValues[id]?.name ?? currentDog.name

✅ ?.
(オプショナルチェーン)を使い、入力がない場合は undefinedを返すように。
✅ ??
(Nullish Coalescing)を使い、 undefined
のときに初期値(currentDog.name)を設定している
age: updatedValues[id]?.age ? Number(updatedValues[id].age) : currentDog.age

✅ ?.
(オプショナルチェーン)を使い、入力がない場合は undefinedを返すように。
✅? : 三項演算子を使い、 undefined
のときに初期値(currentDog.age)を設定
PUT でサーバーに送信
const response = await fetch(`http://localhost:3001/dogs/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData)
});
✅ fetch
で json-server
にデータを送る
✅ method: "PUT"
により、${id}で指定したデータの既存データを上書き
✅ JSON.stringify(newData)
で newData
を JSON
形式に変換
ここの処理はjson-server規定の動きに準じています。
実際はバックエンドアプリのルーティングに沿ってアクセスしたURLごとに処理が動き、その返り値が返ってくることになります。
props.setDogs で state を更新
if (response.ok) {
props.setDogs(
props.dogs.map((dog) => (dog.id === id ? { ...dog, ...newData } : dog))
);
}
response.okがtrueだったときに、props.setDogsの更新用関数を使ってprops.dogsを更新しています。
mapの処理で三項演算子を使い、指定idの犬の場合は内容を更新し、それ以外はもともとの犬の入った新しいdogsを作り、setDogsで更新しています。

例えばスプレッド構文を使わずに下記のように書くと同じようにnameとageとキーの値を更新できます。
{ ...dog, "name": "柴犬", "age": 5}
JSX
最後に戻り値として返すJSXです。
return (
<div>
<h2>🐕 犬の情報を更新</h2>
{props.dogs.map((dog) => (
<div key={dog.id}>
{/* 名前の入力欄 */}
<input
type="text"
value={updatedValues[dog.id]?.name ?? dog.name}
onChange={(e) => handleInputChange(dog.id, "name", e.target.value)}
/>
{/* 年齢の入力欄 */}
<input
type="number"
value={updatedValues[dog.id]?.age ?? dog.age}
onChange={(e) => handleInputChange(dog.id, "age", e.target.value)}
/>
{/* 更新ボタン(1回の通信で名前&年齢を変更) */}
<button onClick={() => updateDogInfo(dog.id)}>更新</button>
</div>
))}
</div>
);
mapメソッド
{props.dogs.map((dog) => (
props.dogsをmapで処理しそれぞれの入力用リストを作成します。
✅ props.dogs
のリストを map()
で1匹ずつ取り出す
✅ dog
に1匹ずつデータが入る
リストのタグ
<div key={dog.id}>
✅ <div>
で各犬の情報を囲む!
✅ key={dog.id}
を入れることで、React がどの犬かわかるようにする(リストの必須ルール)
名前の入力欄(<input>)を作る
<input
type="text"
value={updatedValues[dog.id]?.name ?? dog.name}
onChange={(e) => handleInputChange(dog.id, "name", e.target.value)}
/>
✅ 名前を入力するための <input>
(テキストボックス)を作成
属性 | 意味 |
---|---|
type=”text” | 文字を入力できるテキストボックス |
value={updatedValues[dog.id]?.name ?? dog.name} | 入力値の管理(もし updatedValues に新しい名前があればそれを表示、なければ dog.name を表示) |
onChange={(e) => handleInputChange(dog.id, “name”, e.target.value)} | 入力が変わったら handleInputChange() を実行して、updatedValues に保存 |
ここでも「?」オプショナルチェーンと「??」Nullish Coalescingを使ってupdateValuesの値がない場合は元々の値を初期値として表示されるようにしています。
年齢の入力欄(<input>)を作る
同じく年齢ようの入力欄も作ります。
<input
type="number"
value={updatedValues[dog.id]?.age ?? dog.age}
onChange={(e) => handleInputChange(dog.id, "age", e.target.value)}
/>
✅ 名前と同じように、年齢も入力できる
属性 | 意味 |
---|---|
type=”number” | 数字だけ入力できるボックス |
value={updatedValues[dog.id]?.age ?? dog.age} | 新しく入力された age があればそれを表示、なければ dog.age を表示 |
onChange={(e) => handleInputChange(dog.id, “age”, e.target.value)} | 入力が変わったら handleInputChange() を実行して、updatedValues に保存 |
更新ボタンを作る
<button onClick={() => updateDogInfo(dog.id)}>更新</button>
✅ 「更新」ボタンを押すと updateDogInfo(dog.id)
が実行される
✅ これで PUT
リクエストが送られ、サーバーのデータが更新される
まとめ
以上がデータ更新のコンポーネントでした。色々な書き方が出てきて複雑に感じるかと思いますが、まずは何をやっているのか理解できるように咀嚼してみてください。
コメント