以下の内容はhttps://uga-box.hatenablog.com/entry/2024/12/18/000000より取得しました。


【immer】Proxyの仕組み

immerproxy を使っていることがわかったが、なぜproxy の仕組みが効率的に変更を追跡できるのかをもう少し調べてみた

uga-box.hatenablog.com

1. Proxy の仕組み

Proxy は、JavaScript のネイティブ機能で、あるオブジェクトや関数の操作(プロパティの取得、設定、削除など)を「フック」する

つまり、操作が行われたときにカスタムの処理を差し込むことができる

const target = { name: "Alice" };

const proxy = new Proxy(target, {
  get(target, prop) {
    console.log(`Getting ${prop}`);
    return target[prop];
  },
  set(target, prop, value) {
    console.log(`Setting ${prop} to ${value}`);
    target[prop] = value;
    return true; // 必須: 成功したら true を返す
  },
});

console.log(proxy.name); // "Getting name" と出力、結果: "Alice"
proxy.age = 25; // "Setting age to 25" と出力
console.log(target.age); // 25

2. immer における Proxy の役割

immer は元のオブジェクト(state)を Proxy でラップし、以下の操作を監視する

  • プロパティの取得: 値を読み取る
  • プロパティの設定: 値を書き換える
  • ネストされたオブジェクトへの操作: 深い階層のオブジェクトの操作も追跡

例えば、次のコードを考える:

const state = { user: { name: "Alice", age: 25 } };

const newState = produce(state, (draft) => {
  draft.user.age = 26;
});

このとき、以下のように動作する

  1. 初期状態
    stateProxy でラップして監視を開始

  2. 変更の検知
    draft.user.age = 26 を実行すると、Proxy がこの「書き込み操作」をフックして、user オブジェクトが変更されたことを記録

  3. 部分的な再生成
    immer は「変更があった部分のみ」新しいコピーを生成する。ここでは user オブジェクトが変更されたため、user のコピーを作成し、変更を適用する

  4. 全体の構造を維持
    他の部分(たとえば state のトップレベルオブジェクトや他のプロパティ)はコピーされず、元の参照がそのまま使用される

3. なぜ必要な部分だけ新しいオブジェクトが生成されるのか

Proxy を通じて「どのプロパティが読み取られ、どのプロパティが変更されたか」が完全に追跡されるため、次のような最適化が可能

  1. 変更の検出
    どのプロパティが変更されたかを Proxy 内で記録

  2. 遅延コピー
    必要なタイミングでのみコピーを作成(「書き込みが発生したときだけコピー」)

  3. 構造共有(Structural Sharing)
    未変更部分は元のオブジェクトをそのまま再利用する。このため、パフォーマンスが非常に高い

4. immer の最適化ポイント

  • 最小限のコピー作成
    必要なときに必要な部分だけをコピーすることで、無駄なメモリ使用を削減

  • 構造共有の利用
    未変更部分を再利用するため、大規模なオブジェクトでも効率が良い

  • 読みやすいコードの実現
    Proxy の仕組みにより、ネストされたデータの操作を直感的に記述可能




以上の内容はhttps://uga-box.hatenablog.com/entry/2024/12/18/000000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14