React에서는 mutation을 포함해 어떤 데이터 관리 방식도 사용하실 수 있습니다. 하지만 애플리케이션의 성능이 중요한 부분에서 불변의(immutable) 데이터를 사용할 수 있다면, 쉽게 빠른 shouldComponentUpdate()
메소드를 구현해 애플리케이션의 속도를 크게 향상시킬 수 있습니다.
JavaScript에서 불변성의 데이터를 다루는 것은 Clojure같이 그것을 위해 디자인된 언어로 다루는 것보다는 어렵습니다. 하지만, React는 간단한 불변성 헬퍼를 제공합니다. update()
는 이런 종류의 데이터를 근본적인 변화 없이 쉽게 다루도록 해줍니다. Immutable-js에 관한 좀 더 자세한 정보는 페이스북의 Immutable-js나 성능 심화을 참조하세요.
만약 데이터를 이렇게 변화시킨다면:
myData.x.y.z = 7;
// or...
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
개의 객체만 얕은 복사하고, 나머지는 재사용하기 때문에), 일일히 쓰기엔 큰 고통이 따릅니다. 이 반복들을 보세요! 이건 짜증날 뿐만 아니라 버그들을 야기할수도 있습니다.
update()
는 이런 패턴 속에서 코드를 더 쉽게 쓸 수 있도록 편의 문법을 제공합니다. 코드는 이렇습니다:
var update = require('react-addons-update');
var newData = update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
(MongoDB 쿼리 언어에서 영감을 받은) 이 문법에 익숙해지기에는 시간이 조금 걸리긴 하지만, 쓸모 없는 반복이 없고 정적분석이 가능할 뿐더러 변할 수 있는(mutative) 버전보다 더 많은 타이핑이 필요하지도 않습니다.
$
가 앞에 붙어있는 키들은 커맨드 라고 불립니다. "변하는" 자료 구조는 타겟 이라고 불립니다.
{$push: array}
모든 아이템들을 타겟에 있는 array
에 push()
합니다.{$unshift: array}
타겟속 array
에 있는 모든 아이템들을 unshift()
합니다.{$splice: array of arrays}
arrays
안의 각 아이템들이 splice()
를 주어진 인자들을 사용해 호출하게 합니다.{$set: any}
타겟 전체를 대체합니다.{$merge: object}
타겟과 object
의 키들을 병합합니다.{$apply: function}
는 지금 값을 함수에 전달하고 새로운 리턴 값으로 업데이트합니다.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]}]
이것은 collection
의 인덱스 2
의 키 a
에 접근해, 인덱스 1
에 있는 한 아이템을 접합(splice)해서(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}});
var obj = {a: 5, b: 3};
var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}