コース一覧
React状態管理:カスタムフック・Context・Reducer
カートreducer

React状態管理:カスタムフック・Context・Reducer

カスタムフック、フックのルール、Context、useContext、useReducer、Actionのunion型を扱う全21レッスンの状態管理コースです。fleamaの商品・フォーム・テーマ・ユーザー・カート状態を、再利用可能なロジックと予測可能な状態遷移へ整理します。

1
カスタムフック
01. なぜカスタムフックか10分
02. useItemsを作る15分
03. useFormを作る15分
04. フックのルール15分
05. フックに型を付ける15分
06. つくる - フック化完了20分
07. 第4章クイズ15分
2
useContext
01. バケツリレーの限界15分
02. Contextを作る15分
03. useContextで使う15分
04. テーマの全体共有15分
05. ユーザー情報の共有15分
06. つくる - Context整備20分
07. 第5章クイズ15分
3
useReducer
01. setStateの散らかり15分
02. useReducer15分
03. Actionのunion型15分
04. カートreducer15分
05. 使い分け10分
06. つくる - カート完成20分
07. 第6章クイズ15分

カートreducer

カートの数量と合計

いまの reducer には抜けがある

前のレッスンで書いた reducer は、形としては正しく動いていました。ただしカートの決まりごととしては足りていません。抜けは2つです。

同じスニーカーを2回入れると、行が2つ並びます。fleama のカートで欲しいのは「スニーカー 2点」の1行です。もう1つは、数量を -1 で減らしていくと 0 のまま、あるいはマイナスのままカートに残ることです。

どちらも画面側で頑張るのではなく、reducer の中で決める話です。カートの決まりごとは全部ここに集めます。

既にある行を探してから足す

ADD は「入っていれば増やす、入っていなければ足す」の2通りになります。

TypeScript (JSX)

case "ADD": { const found = cart.find((line) => line.item.id === action.item.id); if (found) { return cart.map((line) => line.item.id === action.item.id ? { ...line, qty: line.qty + 1 } : line, ); } return [...cart, { item: action.item, qty: 1 }]; }

case の中で const を書くときは、波かっこで囲みます。囲まないと、同じ switch の別の case からもその名前が見えてしまい、まとめて怒られます。

0 になったら行ごと外す

CHANGE_QTY は、渡された数量が 0 以下なら REMOVE と同じことをします。

TypeScript (JSX)

case "CHANGE_QTY": if (action.qty <= 0) { return cart.filter((line) => line.item.id !== action.id); } return cart.map((line) => line.item.id === action.id ? { ...line, qty: action.qty } : line, );

ボタン側は何も変わりません。押した人は「1つ減らす」と伝えただけで、それが最後の1つだったかどうかは知らなくてよい。判断は reducer が引き受けるという分け方です。

元の配列を書き換えない

reducer で一番やってしまいがちな間違いが、これです。

TypeScript (JSX)

case "ADD": cart.push({ item: action.item, qty: 1 }); return cart;

これは動きません。返しているのが同じ配列なので、React は「変わっていない」と見て描き直しません。画面が更新されないのに、コンソールにも何も出ません。原因を探しにくい事故なので、reducer では map と filter とスプレッドだけを使う、と決めてしまうのが安全です。

演習

ADD と CHANGE_QTY を、カートの決まりごとに合うように直します。

ヒント

既に入っているかは cart.find((line) => line.item.id === action.item.id) で調べます

case の中で const を使うときは、case ... : { } と波かっこで囲みます

0以下のときの中身は、REMOVE の filter とほとんど同じです

App.tsx
プレビュー

できているか

  • スニーカーを入れると1種類・合計 4800 円になる
  • もう一度入れても行は増えず、数量が2になる
  • -1 で数量が1に戻る
  • もう一度 -1 を押すと行ごと消えてカートが空になる
  • CHANGE_QTY を扱っている
  • push で元の配列を書き換えていない
  • any で逃げていない
エディタを読み込んでいます