- DAY 28:Stateの更新 前半 ― 「一部だけ変える」感覚を身につける
- map()とは ― 「配列を別の形に変換する」ための道具
- 対象データ検索 ― 「どの要素を更新するか」を特定する
- 一部更新 ― 「オブジェクトの一部だけを変える」ための考え方
- 完了状態の変更 ― 実際のコンポーネントで使ってみる
- コードをステップバイステップで分解する
- 練習用テンプレート:DAY 28 前半版「完了状態変更付きToDoリスト」
- DAY 28 前半のまとめ
- DAY 28:Stateの更新 中盤 ― 「mapで探して、部分的に変える」を体に染み込ませる
- map()を「更新用の変換器」として意識する
- 対象データ検索 ― 「どれを変えるか」を明確にする
- 一部更新 ― Spread構文で「コピー+上書き」する
- 完了状態の変更 ― UIとStateのつながりを意識する
- 練習用テンプレート:DAY 28 中盤版「完了状態変更付きToDoリスト」
- DAY 28 中盤のまとめ
- DAY 28:Stateの更新(後半)― 「mapで更新する」を自分の標準パターンにする
- map()を「State更新の型」として固定する
- 対象データ検索を「IDで行う」ことを標準にする
- 一部更新を「コピー+上書き」の形で統一する
- 完了状態の変更を「UIとStateの往復」として理解する
- 練習用テンプレート:DAY 28 後半版「完了状態変更付きToDoリスト」
- DAY 28 後半のまとめ ― 「map+ID+Spread+完了変更」を自分の型にする
DAY 28:Stateの更新 前半 ― 「一部だけ変える」感覚を身につける
DAY 28では、Stateの中の 「一部だけを更新する」 ことにフォーカスして学習していきます。 これまで「追加」「削除」を学んできましたが、現実のアプリでは「状態の変更」が非常に多く登場します。
例えば、タスク管理アプリでの「完了状態の変更」や、買い物リストでの「購入済みフラグの切り替え」などです。 このような「一部更新」を正しく書けるようになると、ReactのState操作が一気に自由になります。
今回の学習項目は次の4つです。
- map()
- 対象データ検索
- 一部更新
- 完了状態の変更
これらを、タスク管理(ToDoリスト)を題材にして、ステップバイステップでかみ砕いていきます。
map()とは ― 「配列を別の形に変換する」ための道具
map()の基本イメージ
まずは、JavaScriptの map() メソッドから整理します。 map() は、「配列の各要素を変換して、新しい配列を作る」 メソッドです。
シンプルな例を見てみます。
const numbers = [1, 2, 3];
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6]
JavaScriptここで起きていることは、
numbersの各要素nに対してn * 2を計算し- その結果を集めた新しい配列
doubledを作っている - 元の配列
numbersは変更されていない
という流れです。
重要ポイント:
map()は「元の配列を壊さず、新しい配列を作る」メソッドです。- ReactのState更新では、「元のStateを直接変更しない」ことが重要なので、
map()は「一部更新」と相性が抜群です。
対象データ検索 ― 「どの要素を更新するか」を特定する
IDで対象を見つける
Stateの中の「一部だけを更新する」には、 まず 「どの要素を更新するか」 を特定する必要があります。
タスク管理アプリの1件分のデータを、次のようなオブジェクトで表現してみます。
const task = {
id: 1,
title: "Reactの勉強",
done: false,
};
JavaScriptこのとき、
id: そのタスクを一意に識別するための番号title: タスクの内容done: 完了状態(true/false)
という役割を持っています。
完了状態を変更したいときは、「この id のタスクの done を変えたい」という形で考えます。
map()の中で対象を探す
map() を使って「対象だけを更新する」には、 次のようなパターンで書きます。
const idToToggle = 1;
const newTasks = tasks.map((task) =>
task.id === idToToggle
? { ...task, done: !task.done }
: task
);
JavaScriptここで起きていることは、
tasksの各要素taskに対してtask.id === idToToggle(対象のIDかどうか)をチェックし- 対象なら
{ ...task, done: !task.done }で「コピー+一部変更」した新しいオブジェクトを返し - 対象でなければ、そのまま
taskを返す
- その結果として、「対象のタスクだけ
doneが反転した新しい配列」ができる
という流れです。
ここが「対象データ検索+一部更新」の基本形になります。
一部更新 ― 「オブジェクトの一部だけを変える」ための考え方
オブジェクトを丸ごと書き換えない
ReactのStateにオブジェクトを入れている場合、 一部だけを変えたいときに、次のように書きたくなることがあります。
// ❌ やってはいけない例
task.done = !task.done;
JavaScriptこれは、元のオブジェクト task を直接書き換えています。 ReactのState更新では、これは避けるべき書き方です。
理由は、
- Reactは「前回のState」と「今回のState」の違いを、 参照の違い(オブジェクトが別物かどうか)で判断することが多いから
- 中身だけを書き換えると、「同じオブジェクトのまま中身だけ変わった」状態になり、 差分検知が難しくなることがあるから
です。
Spread構文で「コピー+一部変更」する
正しい書き方は、 Spread構文(...)を使って「コピー+一部変更」した新しいオブジェクトを作ることです。
const updatedTask = {
...task,
done: !task.done,
};
JavaScriptここで起きていることは、
{ ...task }で、taskの全プロパティをコピーした新しいオブジェクトを作るdone: !task.doneで、その新しいオブジェクトのdoneだけを上書きする
という流れです。
この updatedTask は、元の task とは別のオブジェクトです。 そのため、Reactは「Stateが変わった」と認識しやすくなります。
完了状態の変更 ― 実際のコンポーネントで使ってみる
ToDoリストを題材にする
ここからは、実際のReactコンポーネントで「完了状態の変更」を実装してみます。 題材はシンプルなToDoリストです。
import { useState } from "react";
function TodoList() {
const [title, setTitle] = useState("");
const [tasks, setTasks] = useState([]);
const [nextId, setNextId] = useState(1);
const handleAddTask = () => {
const trimmed = title.trim();
if (trimmed === "") return;
const newTask = {
id: nextId,
title: trimmed,
done: false,
};
setTasks((prevTasks) => [...prevTasks, newTask]);
setNextId((prevId) => prevId + 1);
setTitle("");
};
const handleToggleDone = (id) => {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
};
return (
<div>
<h2>ToDoリスト(Stateの更新 前半)</h2>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="やることを入力してください"
/>
<button onClick={handleAddTask}>追加</button>
<ul style={{ marginTop: "8px" }}>
{tasks.map((task) => (
<li key={task.id}>
<button onClick={() => handleToggleDone(task.id)}>
{task.done ? "✅" : "⬜️"}
</button>
<span
style={{
marginLeft: "8px",
textDecoration: task.done ? "line-through" : "none",
}}
>
{task.title}
</span>
</li>
))}
</ul>
</div>
);
}
export default TodoList;
JSXコードをステップバイステップで分解する
1. 追加処理(復習)
const handleAddTask = () => {
const trimmed = title.trim();
if (trimmed === "") return;
const newTask = {
id: nextId,
title: trimmed,
done: false,
};
setTasks((prevTasks) => [...prevTasks, newTask]);
setNextId((prevId) => prevId + 1);
setTitle("");
};
JSXここはDAY 26・27で学んだ「追加」のパターンです。
- 入力値を整形・バリデーションする
- オブジェクトとして1件分のデータを作る
- Spread構文で新しい配列を作り、Stateに保存する
という流れになっています。
2. 完了状態の変更(今回の主役)
const handleToggleDone = (id) => {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
};
JSXここがDAY 28前半の中心です。
prevTasks.map((task) => ...)→ 「今のタスク一覧」を1件ずつ見て、新しい配列を作るtask.id === id→ 「今回完了状態を変えたいタスクかどうか」を判定する- 対象なら
{ ...task, done: !task.done }→ 「コピー+一部変更」でdoneを反転した新しいオブジェクトを返す - 対象でなければ
taskをそのまま返す → 他のタスクは変更しない
結果として、
「対象のタスクだけ
doneが反転した新しい配列」
ができ、その配列がStateとして保存されます。
3. 表示側で完了状態を反映する
<ul style={{ marginTop: "8px" }}>
{tasks.map((task) => (
<li key={task.id}>
<button onClick={() => handleToggleDone(task.id)}>
{task.done ? "✅" : "⬜️"}
</button>
<span
style={{
marginLeft: "8px",
textDecoration: task.done ? "line-through" : "none",
}}
>
{task.title}
</span>
</li>
))}
</ul>
JSXここでは、
task.done ? "✅" : "⬜️"→ 完了状態に応じて表示アイコンを切り替えています。textDecoration: task.done ? "line-through" : "none"→ 完了したタスクには取り消し線を表示しています。
重要ポイント:
- 「完了状態の変更」は、Stateの更新と表示の両方に関わる操作です。
- State側では
doneを反転し、表示側ではdoneに応じて見た目を変えています。 - どちらも「Stateの現在値」を元にしているため、 Stateが正しく更新されていれば、表示も自然に追従します。
練習用テンプレート:DAY 28 前半版「完了状態変更付きToDoリスト」
最後に、DAY 28前半の学習項目をすべて含んだテンプレートをまとめておきます。
import { useState } from "react";
function TodoList() {
const [title, setTitle] = useState("");
const [tasks, setTasks] = useState([]);
const [nextId, setNextId] = useState(1);
const handleAddTask = () => {
const trimmed = title.trim();
if (trimmed === "") return;
const newTask = {
id: nextId,
title: trimmed,
done: false,
};
setTasks((prevTasks) => [...prevTasks, newTask]);
setNextId((prevId) => prevId + 1);
setTitle("");
};
const handleToggleDone = (id) => {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
};
return (
<div>
<h2>ToDoリスト(Stateの更新 前半)</h2>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="やることを入力してください"
/>
<button onClick={handleAddTask}>追加</button>
<ul style={{ marginTop: "8px" }}>
{tasks.map((task) => (
<li key={task.id}>
<button onClick={() => handleToggleDone(task.id)}>
{task.done ? "✅" : "⬜️"}
</button>
<span
style={{
marginLeft: "8px",
textDecoration: task.done ? "line-through" : "none",
}}
>
{task.title}
</span>
</li>
))}
</ul>
</div>
);
}
export default TodoList;
JSXDAY 28 前半のまとめ
DAY 28前半で身につけてほしいのは、次の感覚です。
map()は「配列の各要素を変換して、新しい配列を作る」ためのメソッドであり、「一部更新」と相性が良いこと- 対象データの検索は、
task.id === idのようにIDで行うのが基本であること - 一部更新は、Spread構文(
{ ...task, done: !task.done })で「コピー+一部変更」した新しいオブジェクトを作る形で行うこと - 完了状態の変更は、「State側で
doneを反転し、表示側でdoneに応じて見た目を変える」という二段構えで考えること
この「一部更新」のパターンが身につくと、
- タスク管理
- 購入済みフラグ
- いいねボタン
- フォロー状態の切り替え
など、あらゆる「状態の変更」を、落ち着いて・安全に・自信を持って書けるようになります。
DAY 28:Stateの更新 中盤 ― 「mapで探して、部分的に変える」を体に染み込ませる
DAY 28の中盤では、前半で触れた 「一部だけを更新する」State操作 を、 より実務に近い形で深く理解していきます。
テーマは引き続き 完了状態の変更(ToDoリスト) ですが、 ここでは次の4つを、もう一段階踏み込んで整理します。
- map()
- 対象データ検索
- 一部更新
- 完了状態の変更
「なんとなく分かる」から「自分で設計できる」まで持っていくイメージで読んでください。
map()を「更新用の変換器」として意識する
単なる「表示用」ではなく「更新用」にも使う
Reactを学び始めると、map() はまず「一覧表示」でよく登場します。
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
JSXここでは、
- 配列
tasksを - JSX(
<li>...</li>)に変換して - 画面に表示する
という用途で使っています。
中盤では、map() を 「State更新のための変換器」 としても意識してみます。
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
JSXここでは、
- 配列
prevTasksを - 「対象だけ一部変更された新しい配列」に変換して
- Stateとして保存しています。
重要な気づき:
map()は「表示用」と「更新用」の両方で使える。 どちらも「配列を別の形に変換する」という同じ役割を持っている。
この感覚があると、map() が一気に頼れる相棒になります。
対象データ検索 ― 「どれを変えるか」を明確にする
IDで対象を特定するのが基本形
Stateの中の「一部だけを更新する」には、 まず 「どの要素を更新するか」 を特定する必要があります。
ToDoリストの1件分のデータは、次のようなオブジェクトだとします。
const task = {
id: 3,
title: "Reactの勉強",
done: false,
};
JavaScript完了状態を変えたいときは、
「idが3のタスクの
doneを反転したい」
という形で考えます。
この「対象の特定」を、map() の中で行います。
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
);
JSXここでの流れは、
prevTasksの各taskを順番に見るtask.id === idで「今回の対象かどうか」を判定する- 対象なら「一部更新した新しいオブジェクト」を返す
- 対象でなければ、そのまま
taskを返す
というものです。
「対象だけ変えて、他はそのまま」がポイント
この書き方のポイントは、
- 対象のタスクだけ
{ ...task, done: !task.done }で新しいオブジェクトにする - 他のタスクはそのまま
taskを返す
という「メリハリ」です。
ここが中盤で意識したいところです。
「全部を変える」のではなく、「対象だけ変えて、他はそのまま残す」。
この発想があると、複雑な更新も落ち着いて書けるようになります。
一部更新 ― Spread構文で「コピー+上書き」する
直接書き換えない理由をもう一度
オブジェクトの一部だけを変えたいとき、 次のように書きたくなることがあります。
// ❌ NG例
task.done = !task.done;
JSXこれは、元の task オブジェクトを直接書き換えています。
ReactのState更新では、これは避けるべきです。
理由は、
- Reactは「前回のState」と「今回のState」の違いを、 オブジェクトの参照(別物かどうか)で判断することが多い
- 中身だけ書き換えると、「同じオブジェクトのまま中身だけ変わった」状態になり、 差分検知が難しくなることがある
からです。
正しい形:Spread構文で新しいオブジェクトを作る
正しい形は、 Spread構文(...)を使って「コピー+一部上書き」した新しいオブジェクトを作ることです。
const updatedTask = {
...task,
done: !task.done,
};
JSXここで起きていることは、
{ ...task }で、taskの全プロパティをコピーした新しいオブジェクトを作るdone: !task.doneで、その新しいオブジェクトのdoneだけを上書きする
という流れです。
この updatedTask は、元の task とは別のオブジェクトです。 そのため、Reactは「Stateが変わった」と認識しやすくなります。
map+Spreadで「一部更新の標準形」ができる
これを map() と組み合わせると、 「一部更新の標準形」が完成します。
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
JSXこの1行の中に、
- 対象データ検索(
task.id === id) - 一部更新(
{ ...task, done: !task.done }) - 不変性の維持(新しい配列+新しいオブジェクト)
がすべて詰まっています。
完了状態の変更 ― UIとStateのつながりを意識する
「ボタン → State更新 → 表示変更」の一本線
完了状態の変更は、 UIとStateがきれいにつながる典型的な例です。
次のコードを見てください。
<ul style={{ marginTop: "8px" }}>
{tasks.map((task) => (
<li key={task.id}>
<button onClick={() => handleToggleDone(task.id)}>
{task.done ? "✅" : "⬜️"}
</button>
<span
style={{
marginLeft: "8px",
textDecoration: task.done ? "line-through" : "none",
}}
>
{task.title}
</span>
</li>
))}
</ul>
JSXここで起きていることを、流れで追ってみます。
- ユーザーが「チェックボタン」をクリックする
onClick={() => handleToggleDone(task.id)}が呼ばれ、 対象のtask.idがhandleToggleDoneに渡されるhandleToggleDoneの中で、setTasks+map+Spread構文により、 対象タスクのdoneが反転した新しい配列がStateとして保存される- Reactが再レンダリングし、
tasks.map(...)が再評価される task.doneに応じて、- ボタンの表示(✅ / ⬜️)
- タイトルのスタイル(取り消し線あり / なし) が切り替わる
ここでの本質:
UIは「Stateの現在値」を映しているだけ。 Stateを正しく更新すれば、UIは自然に追従する。
この感覚があると、「画面をどう変えるか」ではなく、 「Stateをどう変えるか」 に集中できるようになります。
練習用テンプレート:DAY 28 中盤版「完了状態変更付きToDoリスト」
中盤の内容をすべて含んだテンプレートを示します。
import { useState } from "react";
function TodoList() {
const [title, setTitle] = useState("");
const [tasks, setTasks] = useState([]);
const [nextId, setNextId] = useState(1);
const handleAddTask = () => {
const trimmed = title.trim();
if (trimmed === "") return;
const newTask = {
id: nextId,
title: trimmed,
done: false,
};
setTasks((prevTasks) => [...prevTasks, newTask]);
setNextId((prevId) => prevId + 1);
setTitle("");
};
const handleToggleDone = (id) => {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
};
return (
<div>
<h2>ToDoリスト(Stateの更新 中盤)</h2>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="やることを入力してください"
/>
<button onClick={handleAddTask}>追加</button>
<ul style={{ marginTop: "8px" }}>
{tasks.map((task) => (
<li key={task.id}>
<button onClick={() => handleToggleDone(task.id)}>
{task.done ? "✅" : "⬜️"}
</button>
<span
style={{
marginLeft: "8px",
textDecoration: task.done ? "line-through" : "none",
}}
>
{task.title}
</span>
</li>
))}
</ul>
</div>
);
}
export default TodoList;
JSXDAY 28 中盤のまとめ
DAY 28中盤で定着させたいのは、次の4つです。
- map() は「表示用」だけでなく「更新用」としても使える変換器であり、配列を「一部だけ変えた新しい配列」にするのに最適であること
- 対象データ検索 は、
task.id === idのようにIDで行い、「対象だけ変えて、他はそのまま」というメリハリをつけること - 一部更新 は、Spread構文(
{ ...task, done: !task.done })で「コピー+一部上書き」した新しいオブジェクトを作る形で行い、元のオブジェクトを直接書き換えないこと - 完了状態の変更 は、「ボタン → State更新 → 表示変更」という一本の流れで考え、UIはStateの現在値を映しているだけだと理解すること
この「map+ID+Spread+完了状態変更」のパターンが体に染み込むと、
- いいねボタン
- フォロー状態の切り替え
- フラグのON/OFF
- 設定項目の有効/無効
など、あらゆる「一部だけ変える」操作を、 同じ型で落ち着いて書けるようになります。
DAY 28:Stateの更新(後半)― 「mapで更新する」を自分の標準パターンにする
DAY 28の後半では、 これまで学んできた map()+対象データ検索+一部更新+完了状態の変更 を、 「どんなアプリでも使える自分の標準パターン」に仕上げていきます。
題材は引き続き ToDoリスト ですが、 ここでは「完了状態の変更」を軸にしながら、 設計目線・安全性・拡張性 を意識して深掘りしていきます。
map()を「State更新の型」として固定する
表示でも更新でも、mapの役割は同じです
まず、表示側の map() をもう一度見てみます。
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
JSXここでは、
tasks(配列)を<li>...</li>(JSX)に変換して- 新しい配列(JSXの配列)を作っています。
一方、更新側の map() はこうでした。
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
JSXここでも、
prevTasks(配列)を- 「一部だけ更新された
taskオブジェクト」に変換して - 新しい配列(更新後のタスク一覧)を作っています。
共通している本質:
map() は「配列の各要素を変換して、新しい配列を作る」ための道具。 表示でも更新でも、やっていることは同じ。
この感覚があると、 「表示用のmap」と「更新用のmap」が頭の中で一本につながります。
対象データ検索を「IDで行う」ことを標準にする
indexではなくIDを使う理由を、後半でしっかり固める
完了状態を変更するとき、 対象のタスクを特定するために id を使っていました。
task.id === id
JSXここで、index を使う書き方と比較してみます。
// ❌ indexを使った危険な例
setTasks((prevTasks) =>
prevTasks.map((task, index) =>
index === indexToToggle ? { ...task, done: !task.done } : task
)
);
JSX一見動きそうですが、後半の段階ではこれは避けたい書き方です。
理由は、
- 並び替えや途中削除が入ると、「index」と「本来変えたいタスク」がずれてしまう可能性がある
- Reactの再レンダリング時に、
keyがindexだと要素の再利用が誤って行われることがある
など、後からバグの温床になりやすいからです。
IDを使った安全な対象特定
IDを使うと、対象を安定して特定できます。
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
JSXここを自分の標準にしてほしいです。
- 「一覧の1件には必ずIDを持たせる」
- 「対象の特定は
idで行う」
この2つが習慣になると、 削除・更新・詳細表示など、どんな機能を追加してもブレなくなります。
一部更新を「コピー+上書き」の形で統一する
直接書き換えはしない、をもう一度強く意識する
完了状態の変更は、 オブジェクトの一部だけを変える操作です。
NGな書き方はこうでした。
// ❌ NG例:直接書き換え
const handleToggleDone = (id) => {
setTasks((prevTasks) => {
const found = prevTasks.find((task) => task.id === id);
if (found) {
found.done = !found.done; // 直接書き換え
}
return prevTasks;
});
};
JSXこのコードは、
foundが参照しているオブジェクトを直接書き換えているprevTasks自体は「同じ配列のまま」なので、 Reactが変化を検知しにくい
という問題があります。
正しい形:map+Spreadで「新しいオブジェクト」を返す
正しい形は、 map+Spread構文で「コピー+一部上書き」した新しいオブジェクトを返すことです。
const handleToggleDone = (id) => {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
};
JSXここで起きていることは、
- 対象のタスクだけ
{ ...task, done: !task.done }で新しいオブジェクトにする - 他のタスクはそのまま
taskを返す - 結果として、「一部だけ更新された新しい配列」ができる
という流れです。
後半で固めたいポイント:
「一部更新」は、 直接書き換えではなく「コピー+上書き」で行う。 そのための道具が Spread構文。
完了状態の変更を「UIとStateの往復」として理解する
ボタンは「State更新のトリガー」
完了状態の変更は、 UIとStateがきれいにつながる典型的な例です。
<ul style={{ marginTop: "8px" }}>
{tasks.map((task) => (
<li key={task.id}>
<button onClick={() => handleToggleDone(task.id)}>
{task.done ? "✅" : "⬜️"}
</button>
<span
style={{
marginLeft: "8px",
textDecoration: task.done ? "line-through" : "none",
}}
>
{task.title}
</span>
</li>
))}
</ul>
JSXここでの流れをもう一度整理します。
- ユーザーがボタンをクリックする
onClick={() => handleToggleDone(task.id)}が呼ばれ、対象のtask.idが渡されるhandleToggleDoneの中で、setTasks+map+Spread構文により、対象タスクのdoneが反転した新しい配列がStateとして保存される- Reactが再レンダリングし、
tasks.map(...)が再評価される task.doneに応じて、- ボタンの表示(✅ / ⬜️)
- タイトルのスタイル(取り消し線あり / なし) が切り替わる
ここで定着させたい本質:
UIは「Stateの現在値」を映しているだけ。 Stateを正しく更新すれば、UIは自然に変わる。
この一本線が見えていると、 「画面をどういじるか」ではなく、 「Stateをどう設計して、どう更新するか」 に集中できるようになります。
練習用テンプレート:DAY 28 後半版「完了状態変更付きToDoリスト」
DAY 28後半の内容をすべて含んだテンプレートを示します。
import { useState } from "react";
function TodoList() {
const [title, setTitle] = useState("");
const [tasks, setTasks] = useState([]);
const [nextId, setNextId] = useState(1);
const handleAddTask = () => {
const trimmed = title.trim();
if (trimmed === "") return;
const newTask = {
id: nextId,
title: trimmed,
done: false,
};
setTasks((prevTasks) => [...prevTasks, newTask]);
setNextId((prevId) => prevId + 1);
setTitle("");
};
const handleToggleDone = (id) => {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
};
return (
<div>
<h2>ToDoリスト(Stateの更新 後半)</h2>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="やることを入力してください"
/>
<button onClick={handleAddTask}>追加</button>
<ul style={{ marginTop: "8px" }}>
{tasks.map((task) => (
<li key={task.id}>
<button onClick={() => handleToggleDone(task.id)}>
{task.done ? "✅" : "⬜️"}
</button>
<span
style={{
marginLeft: "8px",
textDecoration: task.done ? "line-through" : "none",
}}
>
{task.title}
</span>
</li>
))}
</ul>
</div>
);
}
export default TodoList;
JSXDAY 28 後半のまとめ ― 「map+ID+Spread+完了変更」を自分の型にする
DAY 28後半で、自分の中に固定してほしいのは次の4つです。
- map() は「配列を別の形に変換する」ための道具であり、表示でも更新でも同じ役割を持つこと
- 対象データ検索 は、
task.id === idのようにIDで行い、「対象だけ変えて、他はそのまま」というメリハリをつけること - 一部更新 は、Spread構文(
{ ...task, done: !task.done })で「コピー+一部上書き」した新しいオブジェクトを作る形で行い、直接書き換えないこと - 完了状態の変更 は、「ボタン → State更新 → 再レンダリング → 表示変更」という一本の流れで理解し、UIはStateの結果でしかないと捉えること
この「map+ID+Spread+完了状態変更」の型が自分の中で固まると、
- いいね/いいね解除
- フォロー/フォロー解除
- ON/OFFスイッチ
- 設定の有効/無効
など、あらゆる「一部だけ変える」操作を、 同じパターンで、落ち着いて、安全に書けるようになります。
