不変性とは、状態を直接変更しないことを意味します — 代わりに、変更内容を含む新しいオブジェクト/配列を作成します。これは、React、Redux、および同様のツールが変更を検出してUIを正しく更新する方法の中心です。
ミューテーションの問題
jsx
() {
items.(item);
(items);
}
() {
([...items, item]);
}
React(およびRedux)は、深い内容ではなく参照(oldState === newState)を比較することで変更を検出します — パフォーマンスのため。直接ミューテーションを行うと、参照は同じままなので、フレームワークは何も変更されていないと考え、再レンダリングをスキップします。
// arrays
setItems([...items, newItem]); // add
setItems(items.filter(i => i.id !== id)); // remove
setItems(items.map(i => i.id === id ? { ...i, done: true } : i)); // update one
// objects
setUser({ ...user, name: "Bob" }); // update a field
setUser({ ...user, address: { ...user.address, city: "NY" } }); // nested update
スプレッド演算子は浅いコピーを作成します。ネストされたデータの場合、変更する各レベルをコピーします。
✓ Reliable change detection → correct, efficient re-renders (reference equality)
✓ Enables performance optimizations (React.memo, OnPush, useMemo) that compare references
✓ Predictable state — old state is never altered, easing debugging
✓ Time-travel debugging & undo/redo (keep past state snapshots)
// Immer (used by Redux Toolkit) lets you "mutate" a draft → produces an immutable result
produce(state, draft => { draft.items.push(item); }); // safe + readable
Immerは、ドラフト上でミューテーションのようなコードを書くことを可能にし、それをイミュータブルな更新に変換することで、ネストされたイミュータブル更新を人間工学的にします。
不変性は、参照ベースの変更検出を機能させるメカニズムです — [...items]が再レンダリングをトリガーするがitems.push()はトリガーしない理由です。
正確さを超えて、パフォーマンスの最適化(メモ化は参照を比較)と強力な機能(undo/redo、タイムトラベル)をアンロックします。
イミュータブルな更新パターン(およびImmerのようなツール)を理解することは、React/Reduxで正しく作業するために必須です。状態のミューテーションは初心者の最も一般的なバグの1つです。