イミュータブルなヘルパー
Rectはミュータブル(mutation - 変更可能)を含め、どのようなスタイルのデータ管理でも使用することが出来ます。
ただし、アプリケーションのパフォーマンスが非常に重要になる部分で、イミュータブル(immutable)なデータを使用出来るのであれば、
アプリケーションの速度を大幅に向上するためのshouldComponentUpdate()
メソッドを簡単に実装することが出来ます。
JavaScriptでのイミュータブルなデータの取り扱いは、Clojureのように、
このために設計された言語と比較して難しくなります。
そのため我々は、基本的なデータ表現方法を変更すること無く、
この形式のデータの取り扱いを簡単にするためのシンプルなイミュータブルなヘルパーupdate()
を用意しました。
Immutable-jsのより詳しい情報については、、FacebookのImmutable-jsと、
パフォーママンスの向上のセクションでも確認することが出来ます。
主要な考え方
あなたがデータを次のように変更するとします。
myData.x.y.z = 7;
// または...
myData.a.b.push(9);
この場合、前のコピーが上書きされるため、データが変更されたのかどうかを知るすべがありません。
このようにするのでは無く、myData
の新しいコピーを作成して、変更が必要な部分だけを変更する必要があります。
そうすることで、===
を使用してshouldComponentUpdate()
内でmyData
の古いコピーと新しいコピーの比較をすることが出来ます。
var newData = deepCopy(myData);
newData.x.y.z = 7;
newData.a.b.push(9);
残念ながらディープ・コピーは負荷が高く、不可能なケースも存在しますが、 変更が必要なオブジェクトだけをコピーする事で、そして変更されていないオブジェクトを再利用することで、これを緩和することが出来ます。 それでも残念ながら現在のJavaScriptでは、これは非常に複雑でややこしいものになりかねません。
var newData = extend(myData, {
x: extend(myData.x, {
y: extend(myData.x.y, {z: 7}),
}),
a: extend(myData.a, {b: myData.a.b.concat(9)})
});
これは、パフォーマンスはある程度向上します(log n
オブジェクトだけの浅い(shallow)コピーを行い、残りを再利用)が、
書くのが非常に辛くなります。
この処理全体を繰り返し見返して下さい。
これでは複雑なだけで無く、バグの温床になりかねません。
update()
は、こういったパターンのコードを簡単に書けるようにしてくれるシンプルなシンタックスシュガーを提供します。
先ほどのコードは次のようになります。
var newData = React.addons.update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
冗長性の無いこの文法には少し慣れが必要(MongoDBのクエリー言語からインスパイア)ですが、 静的な分析が可能であり、ミュータブルなものと比べるとタイピングが少なく済みます。
$
の接頭辞を持つキーはコマンドと呼ばれます。
変更可("mutating")のデータ構成は、ターゲット(target)と呼ばれます。
利用可能なコマンド
コマンドの使用例
push
var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
initialArray
は、[1, 2, 3]のままになります。
入れ子のコレクション
var collection = [1, 2, {a: [12, 17, 15]}];
var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
// => [1, 2, {a: [12, 13, 14, 15]}]
これはコレクションのインデックスの2番目であるaのキーにアクセスし、
splice
をインデックスの1番目から1つの項目に対して行い(17を削除)、13と14を挿入します。
現在の値を基にしてその値を更新
var obj = {a: 5, b: 3};
var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
// => {a: 5, b: 6}
// 同じ処理になりますが、より深いネストのコレクションでは冗長になります。
var newObj2 = update(obj, {b: {$set: obj.b * 2}});
(浅い (Shallow))マージ
var obj = {a: 5, b: 3};
var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}