同じデータの2つのstateを同期するより、片方を派生値にする
Reactコンポーネントで「全アイテムのフラットリスト」と「カテゴリ別に分類したリスト」を別々のstateとして持っていた。更新時に片方だけ更新してもう片方を忘れるバグが頻発した。
// Before: 2つの独立したstate
const [items, setItems] = useState<Item[]>([])
const [categorizedItems, setCategorizedItems] = useState<Record<string, Item[]>>({})
// 更新時に両方を手動で同期する必要がある
const handleUpdate = (updated: Item) => {
setItems(prev => prev.map(i => i.id === updated.id ? updated : i))
// ↑ ここだけ更新して setCategorizedItems を忘れると画面に反映されない
}
修正: categorizedItems を正本にして、フラットリストは useMemo で派生させる。
// After: stateは1つ、もう片方は派生値
const [categorizedItems, setCategorizedItems] = useState<Record<string, Item[]>>({})
const allItems = useMemo(
() => Object.values(categorizedItems).flat(),
[categorizedItems]
)
この構造なら setCategorizedItems を更新するだけで allItems も自動的に最新になる。更新ハンドラが増えても「どっちのstateを更新すべきか」で迷わない。
判断基準:
- 2つのstateが同じデータの異なるビューなら、片方を派生値にできないか検討する
- 「更新のたびに2箇所のsetStateを呼ぶ必要がある」コードは、同期漏れのバグを待っている状態
- どちらを正本にするかは「更新の起点がどちらの形式か」で決める。パネルやタブ別に更新するならカテゴリ別を正本にする