diff --git a/docs/docs/09.6-update.md b/docs/docs/09.6-update.md index 2169ba5c1c654..f1ee73b15e8c5 100644 --- a/docs/docs/09.6-update.md +++ b/docs/docs/09.6-update.md @@ -62,6 +62,7 @@ The `$`-prefixed keys are called *commands*. The data structure they are "mutati * `{$set: any}` replace the target entirely. * `{$merge: object}` merge the keys of `object` with the target. * `{$apply: function}` passes in the current value to the function and updates it with the new returned value. + * `{$unset: string} deletes the given key or array/object of keys from the target. If target is an array, values are set to `null`. ## Examples diff --git a/src/addons/__tests__/update-test.js b/src/addons/__tests__/update-test.js index 3c5dda7752557..b8e67435ec692 100644 --- a/src/addons/__tests__/update-test.js +++ b/src/addons/__tests__/update-test.js @@ -79,6 +79,17 @@ describe('update', function() { ); }); + it('should support unset', function() { + expect(update({a:1,b:2}, {$unset:'b'})).toEqual({a:1}); + expect(update({a:1,b:2,c:3}, {$unset:['a','c','d']})).toEqual({b:2}); + expect(update({a:1,b:2,c:3}, {$unset:{'a':'','b':''}})).toEqual({c:3}); + expect(update(['a','b','c'], {$unset:[1,'foo',5]})).toEqual(['a',null,'c']); + expect(update.bind(null, 2, {$unset: 'foo'})).toThrow( + 'Invariant Violation: update(): expected target of $unset to be an ' + + 'object or array; got 2.' + ); + }); + it('should support deep updates', function() { expect(update({a: 'b', c: {d: 'e'}}, {c: {d: {$set: 'f'}}})).toEqual({ a: 'b', @@ -90,7 +101,7 @@ describe('update', function() { expect(update.bind(null, {a: 'b'}, {a: 'c'})).toThrow( 'Invariant Violation: update(): You provided a key path to update() ' + 'that did not contain one of $push, $unshift, $splice, $set, $merge, ' + - '$apply. Did you forget to include {$set: ...}?' + '$apply, $unset. Did you forget to include {$set: ...}?' ); }); }); diff --git a/src/addons/update.js b/src/addons/update.js index dded114ea41fd..e7224da686cce 100644 --- a/src/addons/update.js +++ b/src/addons/update.js @@ -30,6 +30,7 @@ var COMMAND_SPLICE = keyOf({$splice: null}); var COMMAND_SET = keyOf({$set: null}); var COMMAND_MERGE = keyOf({$merge: null}); var COMMAND_APPLY = keyOf({$apply: null}); +var COMMAND_DELETE = keyOf({$unset: null}); var ALL_COMMANDS_LIST = [ COMMAND_PUSH, @@ -37,7 +38,8 @@ var ALL_COMMANDS_LIST = [ COMMAND_SPLICE, COMMAND_SET, COMMAND_MERGE, - COMMAND_APPLY + COMMAND_APPLY, + COMMAND_DELETE ]; var ALL_COMMANDS_SET = {}; @@ -151,6 +153,34 @@ function update(value, spec) { nextValue = spec[COMMAND_APPLY](nextValue); } + if (spec.hasOwnProperty(COMMAND_DELETE)) { + var arg = spec[COMMAND_DELETE], delKeys; + if (Array.isArray(arg)){ + delKeys = arg; + } else if (typeof arg === 'object') { + delKeys = Object.keys(arg); + } else { + delKeys = [arg]; + } + invariant( + nextValue && (Array.isArray(nextValue) || typeof nextValue === 'object'), + 'update(): expected target of %s to be an object or array; got %s.', + COMMAND_DELETE, + nextValue + ); + if (Array.isArray(nextValue)){ + delKeys.forEach(function(i){ + if (nextValue.propertyIsEnumerable(i)) { + nextValue[i] = null; + } + }); + } else { + delKeys.forEach(function(delKey){ + delete nextValue[delKey]; + }); + } + } + for (var k in spec) { if (!(ALL_COMMANDS_SET.hasOwnProperty(k) && ALL_COMMANDS_SET[k])) { nextValue[k] = update(value[k], spec[k]);