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 で書きます。