setStateの散らかり

カートは state が3つの操作を持つ

fleama にカートを付けます。カートに要る操作は3つです。カートに入れる、数量を変える、カートから外す。持ちものは「商品と数量の組」の配列です。

type CartLine = { item: Item; qty: number; };

ここまでで習った道具は useState です。まずはそのまま書いてみます。

useState で書くとこうなる

const [cart, setCart] = useState<CartLine[]>([]); function handleAdd(item: Item) { setCart((prev) => { const found = prev.find((line) => line.item.id === item.id); if (found) { return prev.map((line) => line.item.id === item.id ? { ...line, qty: line.qty + 1 } : line, ); } return [...prev, { item, qty: 1 }]; }); } function handleQtyUp(id: string) { setCart((prev) => prev.map((line) => (line.item.id === id ? { ...line, qty: line.qty + 1 } : line)), ); }

動きます。動きますが、読みにくいところが3つあります。

何が辛いのか

1つめは、同じ規則が2か所に散ることです。「数量を1つ増やす」という同じ計算が handleAdd の中にも handleQtyUp の中にもあります。あとで「数量の上限は10まで」という決まりが増えたら、両方を直すことになります。片方だけ直して事故る、という形で必ず表に出ます。

2つめは、更新の中身が部品の中に散らばることです。カートの規則はアプリの決まりごとなのに、書いてある場所は画面を作る関数の中です。カートを別の画面からも触るようになると、同じ関数を渡して回ることになります。

3つめは、どんな操作があるのか一覧できないことです。handleAdd と handleQtyUp と handleRemove を目で拾って初めて分かります。操作が10個に増えた画面を、後から読む人のことを考えてください。

状態遷移として見る

3つの操作は、言い換えるとこうなります。カートは「いまの中身」と「起きたこと」から「次の中身」が決まる。この形を素直に書ける道具が useReducer です。次のレッスンから使います。

まずは辛さを自分の手で書いて確かめておきます。あとで reducer に書き換えたときの、すっきり具合がまるで違います。

演習

カートの3つの操作を useState で書きます。

ヒント

App.tsx
App.tsx
プレビュー

できているか

  • カートに入れるボタンがある
  • スニーカーを入れると1行増えて合計が 4800 円になる
  • もう一度入れると行は増えず数量が2になる
  • +1 を押すと数量が3になる
  • 削除を押すとカートが空になる