2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおけるイミュータブル設計:破壊的変更を避けるための思考と実践

Posted at

概要

状態を扱うすべてのコードにおいて、**「変更したように見えるが、実際には新しく作り直す」**という発想が必要不可欠だ。
これが、イミュータブル(不変)設計である。

イミュータブルなコードは、副作用がなく、安全で、予測可能で、テストしやすい。
一方で、破壊的な変更(mutate)は、静かにバグの種をまく。

この記事では、JavaScriptでイミュータブル設計を実現するための考え方・具体的な記述・パターンを、現場目線で掘り下げる。


対象環境

モダンJavaScript(ES6〜)環境  
React / Vue / Redux / Zustand / Pinia などの状態管理系ライブラリ利用者も対象

なぜイミュータブル設計が重要なのか?

状態が複数の場所から破壊的に書き換えられると:

  • 意図しない副作用
  • 追跡困難なバグ
  • テスト困難
  • Undo/Redo不可能
  • 変更検知が不安定(Vue / React等)

→ 対策は「状態を直接変更しないこと」。
→ 状態をコピーして新しく作り、そちらを使うこと。


配列のイミュータブル操作

❌ 破壊的操作

const list = [1, 2, 3];
list.push(4); // 元の配列を変更してしまう

✅ 非破壊的操作

const list = [1, 2, 3];
const newList = [...list, 4]; // 新しい配列を作る

他の非破壊パターン:

const updated = list.map(x => x * 2);
const filtered = list.filter(x => x !== 2);

オブジェクトのイミュータブル操作

❌ 破壊的

const user = { name: 'toto', age: 20 };
user.age = 21;

✅ 非破壊的(スプレッド構文)

const updatedUser = { ...user, age: 21 };

ネスト構造の更新

const state = {
  user: {
    profile: {
      name: 'toto',
      age: 20
    }
  }
};

❌ 破壊的

state.user.profile.age = 21;

✅ イミュータブル

const updated = {
  ...state,
  user: {
    ...state.user,
    profile: {
      ...state.user.profile,
      age: 21
    }
  }
};

→ 冗長だが、安全。
→ ✅ Immerなどのライブラリで簡潔に書くことも可能


ライブラリによる支援:Immerの利用

import { produce } from 'immer';

const nextState = produce(state, draft => {
  draft.user.profile.age = 21;
});

→ Immerはイミュータブルな設計を破壊的な記法で書けるライブラリ
→ 内部で差分コピーを行ってくれる


状態管理ライブラリとの関係性

  • Redux, Zustand, Piniaなどは変更を検知するために、イミュータブルな更新が前提
  • Reactの useState でも、直接オブジェクトを書き換えるとレンダリングされない
const [user, setUser] = useState({ name: 'toto' });

user.name = 'bob';     // ❌ 変更検知されない
setUser(user);         // ❌ Reactにとっては「同じオブジェクト」

setUser({ ...user, name: 'bob' }); // ✅ 新しいオブジェクトなので再レンダリング

設計Tips:イミュータブルをデフォルトにするための5原則

  1. 配列操作にはmap/filter/reduceを使う
  2. スプレッド構文を使いこなす
  3. ネスト更新には浅いコピーを段階的に
  4. 状態の変更は関数型的に記述
  5. Immerなどの補助ライブラリで設計の意図を優先

よくある誤解

Q. イミュータブルはパフォーマンスが悪いのでは?

→ ✅ 部分的には事実だが、レンダリング効率やデバッグ性の向上が圧倒的に勝る
→ 特にUIでは「パフォーマンス」よりも「一貫性」が重要。


結語

イミュータブル設計は、ただの記法の違いではない。
それは「状態を信頼できるようにする」ための設計思想だ。

  • 変更は許さず、新しいものを作る
  • 変化は記録され、予測可能になる
  • テストと拡張性が劇的に向上する

状態を破壊せずに運用できるということは、アプリケーションを壊さずに進化させることができるということ。
イミュータブルは、プロダクトを守る防御力であり、設計の未来への投資である。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?