Gazelle
2021年01月19日公開 1227 Views

ネストされたオブジェクトのimmutableな更新方法

react reduxの設計をしていると、stateの状態はimmutableであるべきで、reducerの処理で新規stateオブジェクトを生成する際にstateを直接更新してはならないという制約に出くわす。このルールを怠ると、stateの更新履歴が正しく管理されなくなり、Undo/RedoなどReduxが得意とする処理が壊れるなどの副作用をきたす。

Immutableは分かりにくいが、英訳すると「不変」を意味し、Wikipediaから情報を借りるとimmutableなobjectは次のように記述されている。

イミュータブル なオブジェクトとは、作成後にその状態を変えることのできないオブジェクトのことである。

Reduxで言うと、状態変更前のオブジェクトに変更を加えるべからずという意味となる。前置きは長くなったが、ネストが深くなった時に、前状態を踏襲して新規状態オブジェクトを作成するのは少しテクニックが必要となるため例を示す。

const prevState = {
 a: {
   b: {
     c: {
       d: 1
     }
     e: 2
   }
   f: {
     g: 3
   }
 }
}

というオブジェクトがあり、これを新規状態ではeを3にしたいとする。この時普通に書くとprevState.a.b.e = 3だが、これだとprevStateの値が変わってしまっているため、すなわち状態変更前のオブジェクトに変更が加わっているため問題がある。

Immutableに変更するには次のようにeに向かいネストする度にprevStateの状態をコピーしていく。

const nextState = {
  ...prevState,
  a: {
    ...prevState.a,
    b: {
      ...prevState.a.b,
      e: 3
    }
  }
}

これはObjectの上書きを上手く利用しており、例えば

const obj = {
 a: 1,
 a: 2
};

と定義すると、console.log(objB)の結果は{a: 2}である。すなわち同一のプロパティ名が存在したときには、下に書かれたものが上書きされるというルールをうまく使っていることになる。

ちなみに当たり前だがネストの度に前状態のコピーを怠って

const nextState = {
  ...prevState,
  a: {
    b: {
      e: 3
    }
  }
}
console.log(JSON.stringify(nextState)); // {"a":{"b":{"e":3}}}

とするとprevStateのaプロパティの値が保持できず、fオブジェクトやcオブジェクトが消えてしまう。

パターンとしてはオブジェクトがネストされるたびにネストしたprevStateの値を上に放り込んでいくだけなので、何度かやっていけば機械的にできるようになるだろう。

関連記事

javascriptとjquery、cssを使用してトップへ戻るボタンを作成。考え方も含めて説明していきます。
2020年03月23日
Googleスプレッドシートとスクリプト言語Google App Script(GAS)を使用して青色申告含む確定申告を行ったのでまとめ
2020年03月28日
ReactNativeでアプリを作成してiOS/Androidへリリースするまで説明。アプリ制作はJavascriptを理解していれば簡単であるが、リリースは煩雑で面倒である。
2020年06月01日
commit前に他のコードを実行する方法を解説
2022年04月07日
ホームへ戻る