実例: 「お気に入り」機能
小さな機能を端から端まで作ってみましょう。ユーザーがリスト内の項目をお気に入りとしてマークでき、いくつお気に入りにしたかの件数を見られる、という機能です。
切り分ける
私ならこう垂直ステップに分割します。
- 各項目の隣に星ボタンを追加する(まだ何もしない、ただ表示されるだけ)。
- 星をクリックすると、オン/オフの見た目が切り替わる(状態のみ、保存はしない)。
- トグルを反映した「★ 3 favorites」のカウンターを表示する。
- お気に入りを保存して、ページのリロード後も残るようにする。
各ステップが独立して実行可能で、目に見えることに注目してください。どのステップの後で止めても、動くアプリが手元に残ります。
ステップ1: ボタン
良いプロンプトは、ファイル名、変更内容、そして触らないものを名指しします。
ItemCard コンポーネント(src/components/ItemCard.jsx)で、各カードの
右上隅に星ボタンを追加して。輪郭線の星アイコンをレンダリングして。
まだクリックの挙動はつなげないで — ただ表示されるだけにして。
カードの残りのレイアウトはそのままにして。
アプリを実行します。星が見えますか? いいですね。コミットします。
git add -A && git commit -m "Add star button to item cards"
ステップ2: 状態をトグルする
星ボタンをクリックしたら、塗りつぶしと輪郭線が切り替わるようにして。
これをローカルなコンポーネント状態で管理して — まだどこにも保存しないで。
塗りつぶされた星は、お気に入りを意味する。
状態(state)はアプリの短期記憶だと考えてください — いまこの瞬間に何が真か、たとえば特定の星がいま塗りつぶされているか、といったことです。これであなたは状態とUIを一緒に扱っていますが、これは普通のことです。(UI、つまりユーザーインターフェースとは、人が見てクリックするアプリの部分のことです。)AIはおそらく次のようなものを追加するでしょう。
const [isFavorite, setIsFavorite] = useState(false);
<button onClick={() => setIsFavorite(!isFavorite)}>
{isFavorite ? <StarFilled /> : <StarOutline />}
</button>
星をいくつかクリックしてみます。切り替わりますか? コミット。
ステップ3: カウンター
項目リストの先頭に「★ N favorites」のカウンターを追加して。現在
星が塗りつぶされている項目がいくつあるかを数えて。カウンターから
見えるように必要ならお気に入りの状態をリストコンポーネントへ
引き上げてもいい。
このステップでは、AIに状態の置き場所を再構成してもらう必要があるかもしれません — それは想定内で、まさにそれを独立した小さなステップとしてやる理由です。実行して、星をいくつか切り替え、数字が変わるのを見ます。コミット。
ステップ4: 永続化する
ページのリロード後もお気に入りが残るように永続化して。今は
localStorage を使って — お気に入りの項目 ID のリストが変わるたびに保存し、
起動時に読み込んで。
(localStorageとは、ブラウザが訪問者本人のコンピュータに置いておく小さなメモ帳のようなもので、アプリが何かを書き留めておき、リロード後にもまた見つけられるようにするものです。)ページをリロードします。まだお気に入りのままですか? あなたは今、実行可能なスライスを一つずつ重ねて、機能を出荷したのです。
完成した機能の部品どうしがどう会話するかを見てみましょう。クリックは状態へと流れ下り、画面は状態から描き直され、状態はストレージに写し取られてリロードを生き延びます:
┌─────────────────────────────────────────────┐
│ │
星クリック 状態から
│ (イベント) 描き直す
▼ ▲
┌────────┐ 更新 ┌─────────┐ 供給 ┌────────┐
│ UI │─────────────▶│ 状態 │───────────▶│ UI │
│(ボタン)│ │(アプリの│ │(塗られ │
└────────┘ │ メモリ) │ │た星 + │
└────┬────┘ │カウンタ│
│ 変更時に保存 └────────┘
▼
┌──────────┐
│ストレージ│ ◀── 起動時に読み込み、
│(localSt.)│ だからリロードを生き延びる
└──────────┘