Comparing version 1.2.1 to 1.3.0
# Changelog | ||
### 1.3.0 | ||
* Improved the behavior of recursive producer calls. A producer that is called from another producer is now a no-op; that is, the draft will only be finalized once the outer-most producer completes. Pro and cons of this approach are discussed in [here](https://github.com/mweststrate/immer/issues/100#issuecomment-375216607). Fixes [#100](https://github.com/mweststrate/immer/issues/100) | ||
* Immer no longer relies on `Object.assign` to be present / polyfilled. See[#139](https://github.com/mweststrate/immer/pull/139) by @celebro | ||
* Improved some error messages, see [#144](https://github.com/mweststrate/immer/pull/144) by @btnwtn | ||
### 1.2.1 | ||
@@ -57,3 +63,3 @@ | ||
* Added 'polyfill' for `Symbol`, fixes [#75](https://github.com/mweststrate/immer/issues/75) | ||
* Added 'polyfill' for `Symbol`, fixes [#75](https://github.com/mweststrate/immer/issues/75) | ||
@@ -60,0 +66,0 @@ ### 0.8.2 |
@@ -61,6 +61,15 @@ 'use strict'; | ||
var assign = Object.assign || function assign(target, value) { | ||
for (var key in value) { | ||
if (has(value, key)) { | ||
target[key] = value[key]; | ||
} | ||
} | ||
return target; | ||
}; | ||
function shallowCopy(value) { | ||
if (Array.isArray(value)) return value.slice(); | ||
if (value.__proto__ === undefined) return Object.assign(Object.create(null), value); | ||
return Object.assign({}, value); | ||
var target = value.__proto__ === undefined ? Object.create(null) : {}; | ||
return assign(target, value); | ||
} | ||
@@ -151,3 +160,3 @@ | ||
setPrototypeOf: function setPrototypeOf() { | ||
throw new Error("Don't even try this..."); | ||
throw new Error("Immer does not support `setPrototypeOf()`."); | ||
} | ||
@@ -219,3 +228,3 @@ }; | ||
function defineProperty$1() { | ||
throw new Error("Immer does currently not support defining properties on draft objects"); | ||
throw new Error("Immer does not support defining properties on draft objects."); | ||
} | ||
@@ -235,2 +244,3 @@ | ||
function createProxy(parentState, base) { | ||
if (isProxy(base)) throw new Error("Immer bug. Plz report."); | ||
var state = createState(parentState, base); | ||
@@ -243,2 +253,7 @@ var proxy = Array.isArray(base) ? Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps); | ||
function produceProxy(baseState, producer) { | ||
if (isProxy(baseState)) { | ||
// See #100, don't nest producers | ||
var returnValue = producer.call(baseState, baseState); | ||
return returnValue === undefined ? baseState : returnValue; | ||
} | ||
var previousProxies = proxies; | ||
@@ -250,7 +265,7 @@ proxies = []; | ||
// execute the thunk | ||
var returnValue = producer.call(rootProxy, rootProxy); | ||
var _returnValue = producer.call(rootProxy, rootProxy); | ||
// and finalize the modified proxy | ||
var result = void 0; | ||
// check whether the draft was modified and/or a value was returned | ||
if (returnValue !== undefined && returnValue !== rootProxy) { | ||
if (_returnValue !== undefined && _returnValue !== rootProxy) { | ||
// something was returned, and it wasn't the proxy itself | ||
@@ -262,3 +277,3 @@ if (rootProxy[PROXY_STATE].modified) throw new Error(RETURNED_AND_MODIFIED_ERROR); | ||
// Looks like a wrongly modeled reducer | ||
result = finalize(returnValue); | ||
result = finalize(_returnValue); | ||
} else { | ||
@@ -361,6 +376,6 @@ result = finalize(rootProxy); | ||
function assertUnfinished(state) { | ||
if (state.finished === true) throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process?"); | ||
if (state.finished === true) throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(state.copy || state.base)); | ||
} | ||
// this sounds very expensive, but actually it is not that extensive in practice | ||
// this sounds very expensive, but actually it is not that expensive in practice | ||
// as it will only visit proxies, and only do key-based change detection for objects for | ||
@@ -407,2 +422,7 @@ // which it is not already know that they are changed (that is, only object for which no known key was changed) | ||
function produceEs5(baseState, producer) { | ||
if (isProxy(baseState)) { | ||
// See #100, don't nest producers | ||
var returnValue = producer.call(baseState, baseState); | ||
return returnValue === undefined ? baseState : returnValue; | ||
} | ||
var prevStates = states; | ||
@@ -414,3 +434,3 @@ states = []; | ||
// execute the thunk | ||
var returnValue = producer.call(rootProxy, rootProxy); | ||
var _returnValue = producer.call(rootProxy, rootProxy); | ||
// and finalize the modified proxy | ||
@@ -425,6 +445,6 @@ each(states, function (_, state) { | ||
// check whether the draft was modified and/or a value was returned | ||
if (returnValue !== undefined && returnValue !== rootProxy) { | ||
if (_returnValue !== undefined && _returnValue !== rootProxy) { | ||
// something was returned, and it wasn't the proxy itself | ||
if (rootProxy[PROXY_STATE].modified) throw new Error(RETURNED_AND_MODIFIED_ERROR); | ||
result = finalize(returnValue); | ||
result = finalize(_returnValue); | ||
} else result = finalize(rootProxy); | ||
@@ -431,0 +451,0 @@ // make sure all proxies become unusable |
@@ -57,6 +57,15 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
var assign = Object.assign || function assign(target, value) { | ||
for (var key in value) { | ||
if (has(value, key)) { | ||
target[key] = value[key]; | ||
} | ||
} | ||
return target; | ||
}; | ||
function shallowCopy(value) { | ||
if (Array.isArray(value)) return value.slice(); | ||
if (value.__proto__ === undefined) return Object.assign(Object.create(null), value); | ||
return Object.assign({}, value); | ||
var target = value.__proto__ === undefined ? Object.create(null) : {}; | ||
return assign(target, value); | ||
} | ||
@@ -147,3 +156,3 @@ | ||
setPrototypeOf: function setPrototypeOf() { | ||
throw new Error("Don't even try this..."); | ||
throw new Error("Immer does not support `setPrototypeOf()`."); | ||
} | ||
@@ -215,3 +224,3 @@ }; | ||
function defineProperty$1() { | ||
throw new Error("Immer does currently not support defining properties on draft objects"); | ||
throw new Error("Immer does not support defining properties on draft objects."); | ||
} | ||
@@ -231,2 +240,3 @@ | ||
function createProxy(parentState, base) { | ||
if (isProxy(base)) throw new Error("Immer bug. Plz report."); | ||
var state = createState(parentState, base); | ||
@@ -239,2 +249,7 @@ var proxy = Array.isArray(base) ? Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps); | ||
function produceProxy(baseState, producer) { | ||
if (isProxy(baseState)) { | ||
// See #100, don't nest producers | ||
var returnValue = producer.call(baseState, baseState); | ||
return returnValue === undefined ? baseState : returnValue; | ||
} | ||
var previousProxies = proxies; | ||
@@ -246,7 +261,7 @@ proxies = []; | ||
// execute the thunk | ||
var returnValue = producer.call(rootProxy, rootProxy); | ||
var _returnValue = producer.call(rootProxy, rootProxy); | ||
// and finalize the modified proxy | ||
var result = void 0; | ||
// check whether the draft was modified and/or a value was returned | ||
if (returnValue !== undefined && returnValue !== rootProxy) { | ||
if (_returnValue !== undefined && _returnValue !== rootProxy) { | ||
// something was returned, and it wasn't the proxy itself | ||
@@ -258,3 +273,3 @@ if (rootProxy[PROXY_STATE].modified) throw new Error(RETURNED_AND_MODIFIED_ERROR); | ||
// Looks like a wrongly modeled reducer | ||
result = finalize(returnValue); | ||
result = finalize(_returnValue); | ||
} else { | ||
@@ -357,6 +372,6 @@ result = finalize(rootProxy); | ||
function assertUnfinished(state) { | ||
if (state.finished === true) throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process?"); | ||
if (state.finished === true) throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(state.copy || state.base)); | ||
} | ||
// this sounds very expensive, but actually it is not that extensive in practice | ||
// this sounds very expensive, but actually it is not that expensive in practice | ||
// as it will only visit proxies, and only do key-based change detection for objects for | ||
@@ -403,2 +418,7 @@ // which it is not already know that they are changed (that is, only object for which no known key was changed) | ||
function produceEs5(baseState, producer) { | ||
if (isProxy(baseState)) { | ||
// See #100, don't nest producers | ||
var returnValue = producer.call(baseState, baseState); | ||
return returnValue === undefined ? baseState : returnValue; | ||
} | ||
var prevStates = states; | ||
@@ -410,3 +430,3 @@ states = []; | ||
// execute the thunk | ||
var returnValue = producer.call(rootProxy, rootProxy); | ||
var _returnValue = producer.call(rootProxy, rootProxy); | ||
// and finalize the modified proxy | ||
@@ -421,6 +441,6 @@ each(states, function (_, state) { | ||
// check whether the draft was modified and/or a value was returned | ||
if (returnValue !== undefined && returnValue !== rootProxy) { | ||
if (_returnValue !== undefined && _returnValue !== rootProxy) { | ||
// something was returned, and it wasn't the proxy itself | ||
if (rootProxy[PROXY_STATE].modified) throw new Error(RETURNED_AND_MODIFIED_ERROR); | ||
result = finalize(returnValue); | ||
result = finalize(_returnValue); | ||
} else result = finalize(rootProxy); | ||
@@ -427,0 +447,0 @@ // make sure all proxies become unusable |
@@ -1,2 +0,2 @@ | ||
var e,r;e=this,r=function(e){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n="undefined"!=typeof Symbol?Symbol("immer-proxy-state"):"__$immer_state",t="An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.";var o=!("undefined"!=typeof process&&"production"===process.env.NODE_ENV||"verifyMinified"!==function(){}.name),i="undefined"!=typeof Proxy;function f(e){return!!e&&!!e[n]}function u(e){if(!e)return!1;if("object"!==(void 0===e?"undefined":r(e)))return!1;if(Array.isArray(e))return!0;var n=Object.getPrototypeOf(e);return null===n||n===Object.prototype}function a(e){return o&&Object.freeze(e),e}function c(e){return Array.isArray(e)?e.slice():void 0===e.__proto__?Object.assign(Object.create(null),e):Object.assign({},e)}function s(e,r){if(Array.isArray(e))for(var n=0;n<e.length;n++)r(n,e[n]);else for(var t in e)r(t,e[t])}function d(e,r){return Object.prototype.hasOwnProperty.call(e,r)}function p(e){if(f(e)){var r=e[n];return!0===r.modified?!0===r.finalized?r.copy:(r.finalized=!0,t=i?r.copy:r.copy=c(e),o=r.base,s(t,function(e,r){r!==o[e]&&(t[e]=p(r))}),a(t)):r.base}var t,o;return function e(r){if(!u(r))return;if(Object.isFrozen(r))return;s(r,function(n,t){f(t)?r[n]=p(t):e(t)});a(r)}(e),e}function y(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}var l=null,b={get:function(e,r){if(r===n)return e;if(e.modified){var t=e.copy[r];return t===e.base[r]&&u(t)?e.copy[r]=g(e,t):t}if(d(e.proxies,r))return e.proxies[r];var o=e.base[r];return!f(o)&&u(o)?e.proxies[r]=g(e,o):o},has:function(e,r){return r in m(e)},ownKeys:function(e){return Reflect.ownKeys(m(e))},set:function(e,r,n){if(!e.modified){if(r in e.base&&y(e.base[r],n)||d(e.proxies,r)&&e.proxies[r]===n)return!0;h(e)}return e.copy[r]=n,!0},deleteProperty:function(e,r){return h(e),delete e.copy[r],!0},getOwnPropertyDescriptor:function(e,r){var n=e.modified?e.copy:d(e.proxies,r)?e.proxies:e.base,t=Reflect.getOwnPropertyDescriptor(n,r);!t||Array.isArray(n)&&"length"===r||(t.configurable=!0);return t},defineProperty:function(){throw new Error("Immer does currently not support defining properties on draft objects")},setPrototypeOf:function(){throw new Error("Don't even try this...")}},v={};function m(e){return!0===e.modified?e.copy:e.base}function h(e){e.modified||(e.modified=!0,e.copy=c(e.base),Object.assign(e.copy,e.proxies),e.parent&&h(e.parent))}function g(e,r){var n={modified:!1,finalized:!1,parent:e,base:r,copy:void 0,proxies:{}},t=Array.isArray(r)?Proxy.revocable([n],v):Proxy.revocable(n,b);return l.push(t),t.proxy}s(b,function(e,r){v[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}});var w={},j=null;function O(e){return e.hasCopy?e.copy:e.base}function x(e){e.modified||(e.modified=!0,e.parent&&x(e.parent))}function P(e){e.hasCopy||(e.hasCopy=!0,e.copy=c(e.base))}function A(e,r){var t=c(r);s(r,function(e){var r;Object.defineProperty(t,""+e,w[r=""+e]||(w[r]={configurable:!0,enumerable:!0,get:function(){return function(e,r){E(e);var n=O(e)[r];return!e.finalizing&&n===e.base[r]&&u(n)?(P(e),e.copy[r]=A(e,n)):n}(this[n],r)},set:function(e){!function(e,r,n){if(E(e),!e.modified){if(y(O(e)[r],n))return;x(e),P(e)}e.copy[r]=n}(this[n],r,e)}}))});var o,i,f,a={modified:!1,hasCopy:!1,parent:e,base:r,proxy:t,copy:void 0,finished:!1,finalizing:!1,finalized:!1};return o=t,i=n,f=a,Object.defineProperty(o,i,{value:f,enumerable:!1,writable:!0}),j.push(a),t}function E(e){if(!0===e.finished)throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process?")}function z(e){var r=e.proxy;if(r.length!==e.base.length)return!0;var n=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!n||n.get)}function _(e,o){var i=j;j=[];try{var f=A(void 0,e),u=o.call(f,f);s(j,function(e,r){r.finalizing=!0}),function(){for(var e=j.length-1;e>=0;e--){var n=j[e];!1===n.modified&&(Array.isArray(n.base)?z(n)&&x(n):(t=n,o=Object.keys(t.base),i=Object.keys(t.proxy),function(e,n){if(y(e,n))return!0;if("object"!==(void 0===e?"undefined":r(e))||null===e||"object"!==(void 0===n?"undefined":r(n))||null===n)return!1;var t=Object.keys(e),o=Object.keys(n);if(t.length!==o.length)return!1;for(var i=0;i<t.length;i++)if(!hasOwnProperty.call(n,t[i])||!y(e[t[i]],n[t[i]]))return!1;return!0}(o,i)||x(n)))}var t,o,i}();var a=void 0;if(void 0!==u&&u!==f){if(f[n].modified)throw new Error(t);a=p(u)}else a=p(f);return s(j,function(e,r){r.finished=!0}),a}finally{j=i}}e.default=function e(o,f){if(1!==arguments.length&&2!==arguments.length)throw new Error("produce expects 1 or 2 arguments, got "+arguments.length);if("function"==typeof o){if("function"==typeof f)throw new Error("if first argument is a function (curried invocation), the second argument to produce cannot be a function");var a=f,c=o;return function(){var r=arguments;return e(void 0===r[0]&&void 0!==a?a:r[0],function(e){return r[0]=e,c.apply(e,r)})}}if("function"!=typeof f)throw new Error("if first argument is not a function, the second argument to produce should be a function");if("object"!==(void 0===o?"undefined":r(o))||null===o)return f(o);if(!u(o))throw new Error("the first argument to an immer producer should be a primitive, plain object or array, got "+(void 0===o?"undefined":r(o))+': "'+o+'"');return i?function(e,r){var o=l;l=[];try{var i=g(void 0,e),f=r.call(i,i),u=void 0;if(void 0!==f&&f!==i){if(i[n].modified)throw new Error(t);u=p(f)}else u=p(i);return s(l,function(e,r){return r.revoke()}),u}finally{l=o}}(o,f):_(o,f)},e.setAutoFreeze=function(e){o=e},e.setUseProxies=function(e){i=e},Object.defineProperty(e,"__esModule",{value:!0})},"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(e.immer={}); | ||
var e,r;e=this,r=function(e){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n="undefined"!=typeof Symbol?Symbol("immer-proxy-state"):"__$immer_state",t="An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.";var o=!("undefined"!=typeof process&&"production"===process.env.NODE_ENV||"verifyMinified"!==function(){}.name),i="undefined"!=typeof Proxy;function f(e){return!!e&&!!e[n]}function u(e){if(!e)return!1;if("object"!==(void 0===e?"undefined":r(e)))return!1;if(Array.isArray(e))return!0;var n=Object.getPrototypeOf(e);return null===n||n===Object.prototype}function a(e){return o&&Object.freeze(e),e}var c=Object.assign||function(e,r){for(var n in r)p(r,n)&&(e[n]=r[n]);return e};function s(e){if(Array.isArray(e))return e.slice();var r=void 0===e.__proto__?Object.create(null):{};return c(r,e)}function d(e,r){if(Array.isArray(e))for(var n=0;n<e.length;n++)r(n,e[n]);else for(var t in e)r(t,e[t])}function p(e,r){return Object.prototype.hasOwnProperty.call(e,r)}function y(e){if(f(e)){var r=e[n];return!0===r.modified?!0===r.finalized?r.copy:(r.finalized=!0,t=i?r.copy:r.copy=s(e),o=r.base,d(t,function(e,r){r!==o[e]&&(t[e]=y(r))}),a(t)):r.base}var t,o;return function e(r){if(!u(r))return;if(Object.isFrozen(r))return;d(r,function(n,t){f(t)?r[n]=y(t):e(t)});a(r)}(e),e}function l(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}var v=null,b={get:function(e,r){if(r===n)return e;if(e.modified){var t=e.copy[r];return t===e.base[r]&&u(t)?e.copy[r]=w(e,t):t}if(p(e.proxies,r))return e.proxies[r];var o=e.base[r];return!f(o)&&u(o)?e.proxies[r]=w(e,o):o},has:function(e,r){return r in h(e)},ownKeys:function(e){return Reflect.ownKeys(h(e))},set:function(e,r,n){if(!e.modified){if(r in e.base&&l(e.base[r],n)||p(e.proxies,r)&&e.proxies[r]===n)return!0;g(e)}return e.copy[r]=n,!0},deleteProperty:function(e,r){return g(e),delete e.copy[r],!0},getOwnPropertyDescriptor:function(e,r){var n=e.modified?e.copy:p(e.proxies,r)?e.proxies:e.base,t=Reflect.getOwnPropertyDescriptor(n,r);!t||Array.isArray(n)&&"length"===r||(t.configurable=!0);return t},defineProperty:function(){throw new Error("Immer does not support defining properties on draft objects.")},setPrototypeOf:function(){throw new Error("Immer does not support `setPrototypeOf()`.")}},m={};function h(e){return!0===e.modified?e.copy:e.base}function g(e){e.modified||(e.modified=!0,e.copy=s(e.base),Object.assign(e.copy,e.proxies),e.parent&&g(e.parent))}function w(e,r){if(f(r))throw new Error("Immer bug. Plz report.");var n={modified:!1,finalized:!1,parent:e,base:r,copy:void 0,proxies:{}},t=Array.isArray(r)?Proxy.revocable([n],m):Proxy.revocable(n,b);return v.push(t),t.proxy}d(b,function(e,r){m[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}});var O={},j=null;function x(e){return e.hasCopy?e.copy:e.base}function P(e){e.modified||(e.modified=!0,e.parent&&P(e.parent))}function A(e){e.hasCopy||(e.hasCopy=!0,e.copy=s(e.base))}function E(e,r){var t=s(r);d(r,function(e){var r;Object.defineProperty(t,""+e,O[r=""+e]||(O[r]={configurable:!0,enumerable:!0,get:function(){return function(e,r){z(e);var n=x(e)[r];return!e.finalizing&&n===e.base[r]&&u(n)?(A(e),e.copy[r]=E(e,n)):n}(this[n],r)},set:function(e){!function(e,r,n){if(z(e),!e.modified){if(l(x(e)[r],n))return;P(e),A(e)}e.copy[r]=n}(this[n],r,e)}}))});var o,i,f,a={modified:!1,hasCopy:!1,parent:e,base:r,proxy:t,copy:void 0,finished:!1,finalizing:!1,finalized:!1};return o=t,i=n,f=a,Object.defineProperty(o,i,{value:f,enumerable:!1,writable:!0}),j.push(a),t}function z(e){if(!0===e.finished)throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? "+JSON.stringify(e.copy||e.base))}function _(e){var r=e.proxy;if(r.length!==e.base.length)return!0;var n=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!n||n.get)}function S(e,o){if(f(e)){var i=o.call(e,e);return void 0===i?e:i}var u=j;j=[];try{var a=E(void 0,e),c=o.call(a,a);d(j,function(e,r){r.finalizing=!0}),function(){for(var e=j.length-1;e>=0;e--){var n=j[e];!1===n.modified&&(Array.isArray(n.base)?_(n)&&P(n):(t=n,o=Object.keys(t.base),i=Object.keys(t.proxy),function(e,n){if(l(e,n))return!0;if("object"!==(void 0===e?"undefined":r(e))||null===e||"object"!==(void 0===n?"undefined":r(n))||null===n)return!1;var t=Object.keys(e),o=Object.keys(n);if(t.length!==o.length)return!1;for(var i=0;i<t.length;i++)if(!hasOwnProperty.call(n,t[i])||!l(e[t[i]],n[t[i]]))return!1;return!0}(o,i)||P(n)))}var t,o,i}();var s=void 0;if(void 0!==c&&c!==a){if(a[n].modified)throw new Error(t);s=y(c)}else s=y(a);return d(j,function(e,r){r.finished=!0}),s}finally{j=u}}e.default=function e(o,a){if(1!==arguments.length&&2!==arguments.length)throw new Error("produce expects 1 or 2 arguments, got "+arguments.length);if("function"==typeof o){if("function"==typeof a)throw new Error("if first argument is a function (curried invocation), the second argument to produce cannot be a function");var c=a,s=o;return function(){var r=arguments;return e(void 0===r[0]&&void 0!==c?c:r[0],function(e){return r[0]=e,s.apply(e,r)})}}if("function"!=typeof a)throw new Error("if first argument is not a function, the second argument to produce should be a function");if("object"!==(void 0===o?"undefined":r(o))||null===o)return a(o);if(!u(o))throw new Error("the first argument to an immer producer should be a primitive, plain object or array, got "+(void 0===o?"undefined":r(o))+': "'+o+'"');return i?function(e,r){if(f(e)){var o=r.call(e,e);return void 0===o?e:o}var i=v;v=[];try{var u=w(void 0,e),a=r.call(u,u),c=void 0;if(void 0!==a&&a!==u){if(u[n].modified)throw new Error(t);c=y(a)}else c=y(u);return d(v,function(e,r){return r.revoke()}),c}finally{v=i}}(o,a):S(o,a)},e.setAutoFreeze=function(e){o=e},e.setUseProxies=function(e){i=e},Object.defineProperty(e,"__esModule",{value:!0})},"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(e.immer={}); | ||
//# sourceMappingURL=immer.umd.js.map |
{ | ||
"name": "immer", | ||
"version": "1.2.1", | ||
"version": "1.3.0", | ||
"description": "Create your next immutable state by mutating the current one", | ||
@@ -12,2 +12,3 @@ "main": "dist/immer.js", | ||
"scripts": { | ||
"watch": "jest --watch", | ||
"test": "jest", | ||
@@ -14,0 +15,0 @@ "test:perf": "yarn-or-npm build && node --expose-gc node_modules/jest-cli/bin/jest.js --verbose --testRegex '__performance_tests__/.*?js$'", |
251
readme.md
# Immer | ||
[![npm](https://img.shields.io/npm/v/immer.svg)](https://www.npmjs.com/package/immer) | ||
[![size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js?compression=gzip)](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js) | ||
[![Build Status](https://travis-ci.org/mweststrate/immer.svg?branch=master)](https://travis-ci.org/mweststrate/immer) | ||
[![Coverage Status](https://coveralls.io/repos/github/mweststrate/immer/badge.svg?branch=master)](https://coveralls.io/github/mweststrate/immer?branch=master) | ||
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) | ||
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michelweststrate) | ||
[![npm](https://img.shields.io/npm/v/immer.svg)](https://www.npmjs.com/package/immer) [![size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js?compression=gzip)](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js) [![Build Status](https://travis-ci.org/mweststrate/immer.svg?branch=master)](https://travis-ci.org/mweststrate/immer) [![Coverage Status](https://coveralls.io/repos/github/mweststrate/immer/badge.svg?branch=master)](https://coveralls.io/github/mweststrate/immer?branch=master) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michelweststrate) | ||
@@ -17,4 +12,4 @@ _Create the next immutable state tree by simply modifying the current tree_ | ||
* CDN: Exposed global is `immer` | ||
* Unpkg: `<script src="https://unpkg.com/immer/dist/immer.umd.js"></script>` | ||
* JSDelivr: `<script src="https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js"></script>` | ||
* Unpkg: `<script src="https://unpkg.com/immer/dist/immer.umd.js"></script>` | ||
* JSDelivr: `<script src="https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js"></script>` | ||
@@ -26,8 +21,5 @@ --- | ||
Immer (German for: always) is a tiny package that allows you to work with immutable state in a more convenient way. | ||
It is based on the [_copy-on-write_](https://en.wikipedia.org/wiki/Copy-on-write) mechanism. | ||
Immer (German for: always) is a tiny package that allows you to work with immutable state in a more convenient way. It is based on the [_copy-on-write_](https://en.wikipedia.org/wiki/Copy-on-write) mechanism. | ||
The basic idea is that you will apply all your changes to a temporarily _draftState_, which is a proxy of the _currentState_. | ||
Once all your mutations are completed, Immer will produce the _nextState_ based on the mutations to the draft state. | ||
This means that you can interact with your data by simply modifying it, while keeping all the benefits of immutable data. | ||
The basic idea is that you will apply all your changes to a temporarily _draftState_, which is a proxy of the _currentState_. Once all your mutations are completed, Immer will produce the _nextState_ based on the mutations to the draft state. This means that you can interact with your data by simply modifying it, while keeping all the benefits of immutable data. | ||
@@ -65,3 +57,3 @@ ![immer-hd.png](images/hd/immer.png) | ||
const nextState = produce(baseState, draftState => { | ||
draftState.push({ todo: "Tweet about it" }) | ||
draftState.push({todo: "Tweet about it"}) | ||
draftState[1].done = true | ||
@@ -109,14 +101,14 @@ }) | ||
const byId = (state, action) => { | ||
switch (action.type) { | ||
case RECEIVE_PRODUCTS: | ||
return { | ||
...state, | ||
...action.products.reduce((obj, product) => { | ||
obj[product.id] = product | ||
return obj | ||
}, {}) | ||
} | ||
default: | ||
return state | ||
} | ||
switch (action.type) { | ||
case RECEIVE_PRODUCTS: | ||
return { | ||
...state, | ||
...action.products.reduce((obj, product) => { | ||
obj[product.id] = product | ||
return obj | ||
}, {}) | ||
} | ||
default: | ||
return state | ||
} | ||
} | ||
@@ -128,13 +120,13 @@ ``` | ||
```javascript | ||
import produce from 'immer' | ||
import produce from "immer" | ||
const byId = (state, action) => | ||
produce(state, draft => { | ||
switch (action.type) { | ||
case RECEIVE_PRODUCTS: | ||
action.products.forEach(product => { | ||
draft[product.id] = product | ||
}) | ||
} | ||
}) | ||
produce(state, draft => { | ||
switch (action.type) { | ||
case RECEIVE_PRODUCTS: | ||
action.products.forEach(product => { | ||
draft[product.id] = product | ||
}) | ||
} | ||
}) | ||
``` | ||
@@ -144,5 +136,3 @@ | ||
Creating Redux reducer is just a sample application of the Immer package. | ||
Immer is not just designed to simplify Redux reducers. | ||
It can be used in any context where you have an immutable data tree that you want to clone and modify (with structural sharing). | ||
Creating Redux reducer is just a sample application of the Immer package. Immer is not just designed to simplify Redux reducers. It can be used in any context where you have an immutable data tree that you want to clone and modify (with structural sharing). | ||
@@ -153,4 +143,3 @@ _Note: it might be tempting after using producers for a while, to just place `produce` in your root reducer and then pass the draft to each reducer and work directly over such draft. Don't do that. It kills the point of Redux where each reducer is testable as pure reducer. Immer is best used when applying it to small individual pieces of logic._ | ||
Deep updates in the state of React components can be greatly simplified as well by using immer. | ||
Take for example the following onClick handlers (Try in [codesandbox](https://codesandbox.io/s/m4yp57632j)): | ||
Deep updates in the state of React components can be greatly simplified as well by using immer. Take for example the following onClick handlers (Try in [codesandbox](https://codesandbox.io/s/m4yp57632j)): | ||
@@ -162,3 +151,3 @@ ```javascript | ||
onBirthDayClick1 = () => { | ||
this.setState((prevState)=>({ | ||
this.setState(prevState => ({ | ||
user: { | ||
@@ -174,7 +163,9 @@ ...prevState.user, | ||
* we can just create a curried producer and further simplify! | ||
*/ | ||
*/ | ||
onBirthDayClick2 = () => { | ||
this.setState(produce(draft => { | ||
draft.user.age += 1 | ||
})) | ||
this.setState( | ||
produce(draft => { | ||
draft.user.age += 1 | ||
}) | ||
) | ||
} | ||
@@ -185,4 +176,3 @@ ``` | ||
Passing a function as the first argument to `produce` is intended to be used for currying. This means that you get a pre-bound producer that only needs a state to produce the value from. | ||
The producer function gets passed in the draft, and any further arguments that were passed to the curried function. | ||
Passing a function as the first argument to `produce` is intended to be used for currying. This means that you get a pre-bound producer that only needs a state to produce the value from. The producer function gets passed in the draft, and any further arguments that were passed to the curried function. | ||
@@ -224,17 +214,17 @@ For example: | ||
```javascript | ||
import produce from 'immer' | ||
import produce from "immer" | ||
const byId = produce( | ||
(draft, action) => { | ||
switch (action.type) { | ||
case RECEIVE_PRODUCTS: | ||
action.products.forEach(product => { | ||
draft[product.id] = product | ||
}) | ||
return | ||
(draft, action) => { | ||
switch (action.type) { | ||
case RECEIVE_PRODUCTS: | ||
action.products.forEach(product => { | ||
draft[product.id] = product | ||
}) | ||
return | ||
} | ||
}, | ||
{ | ||
1: {id: 1, name: "product-1"} | ||
} | ||
}, | ||
{ | ||
1: { id: 1, name: "product-1" } | ||
} | ||
) | ||
@@ -245,49 +235,42 @@ ``` | ||
Immer automatically freezes any state trees that are modified using `produce`. | ||
This protects against accidental modifications of the state tree outside of a producer. | ||
This comes with a performance impact, so it is recommended to disable this option in production. | ||
It is by default enabled. | ||
By default it is turned on during local development, and turned off in production. | ||
Use `setAutoFreeze(true / false)` to explicitly turn this feature on or off. | ||
Immer automatically freezes any state trees that are modified using `produce`. This protects against accidental modifications of the state tree outside of a producer. This comes with a performance impact, so it is recommended to disable this option in production. It is by default enabled. By default it is turned on during local development, and turned off in production. Use `setAutoFreeze(true / false)` to explicitly turn this feature on or off. | ||
## Returning data from producers | ||
It is not needed to return anything from a producer, as Immer will return the (finalized) version of the `draft` anyway. | ||
However, it is allowed to just `return draft`. | ||
It is not needed to return anything from a producer, as Immer will return the (finalized) version of the `draft` anyway. However, it is allowed to just `return draft`. | ||
It is also allowed to return arbitrarily other data from the producer function. But _only_ if you didn't modify the draft. | ||
This can be useful to produce an entirely new state. Some examples: | ||
It is also allowed to return arbitrarily other data from the producer function. But _only_ if you didn't modify the draft. This can be useful to produce an entirely new state. Some examples: | ||
```javascript | ||
const userReducer = produce((draft, action) => { | ||
switch (action.type) { | ||
case "renameUser": | ||
// OK: we modify the current state | ||
draft.users[action.payload.id].name = action.payload.name | ||
return draft // same as just 'return' | ||
case "loadUsers": | ||
// OK: we return an entirely new state | ||
return action.payload | ||
case "adduser-1": | ||
// NOT OK: This doesn't do change the draft nor return a new state! | ||
// It doesn't modify the draft (it just redeclares it) | ||
// In fact, this just doesn't do anything at all | ||
draft = { users: [...draft.users, action.payload]} | ||
return | ||
case "adduser-2": | ||
// NOT OK: modifying draft *and* returning a new state | ||
draft.userCount += 1 | ||
return { users: [...draft.users, action.payload] } | ||
case "adduser-3": | ||
// OK: returning a new state. But, unnecessary complex and expensive | ||
return { | ||
userCount: draft.userCount + 1, | ||
users: [...draft.users, action.payload] | ||
} | ||
case "adduser-4": | ||
// OK: the immer way | ||
draft.userCount += 1 | ||
draft.push(action.payload) | ||
return | ||
} | ||
switch (action.type) { | ||
case "renameUser": | ||
// OK: we modify the current state | ||
draft.users[action.payload.id].name = action.payload.name | ||
return draft // same as just 'return' | ||
case "loadUsers": | ||
// OK: we return an entirely new state | ||
return action.payload | ||
case "adduser-1": | ||
// NOT OK: This doesn't do change the draft nor return a new state! | ||
// It doesn't modify the draft (it just redeclares it) | ||
// In fact, this just doesn't do anything at all | ||
draft = {users: [...draft.users, action.payload]} | ||
return | ||
case "adduser-2": | ||
// NOT OK: modifying draft *and* returning a new state | ||
draft.userCount += 1 | ||
return {users: [...draft.users, action.payload]} | ||
case "adduser-3": | ||
// OK: returning a new state. But, unnecessary complex and expensive | ||
return { | ||
userCount: draft.userCount + 1, | ||
users: [...draft.users, action.payload] | ||
} | ||
case "adduser-4": | ||
// OK: the immer way | ||
draft.userCount += 1 | ||
draft.users.push(action.payload) | ||
return | ||
} | ||
}) | ||
@@ -303,6 +286,6 @@ ``` | ||
```javascript | ||
const base = { counter: 0 } | ||
const base = {counter: 0} | ||
const next = produce(base, function() { | ||
this.counter++ | ||
this.counter++ | ||
}) | ||
@@ -313,3 +296,3 @@ console.log(next.counter) // 1 | ||
const increment = produce(function() { | ||
this.counter++ | ||
this.counter++ | ||
}) | ||
@@ -325,6 +308,3 @@ console.log(increment(base).counter) // 1 | ||
By default `produce` tries to use proxies for optimal performance. | ||
However, on older JavaScript engines `Proxy` is not available. | ||
For example, when running Microsoft Internet Explorer or React Native on Android. | ||
In such cases Immer will fallback to an ES5 compatible implementation which works identical, but is a bit slower. | ||
By default `produce` tries to use proxies for optimal performance. However, on older JavaScript engines `Proxy` is not available. For example, when running Microsoft Internet Explorer or React Native on Android. In such cases Immer will fallback to an ES5 compatible implementation which works identical, but is a bit slower. | ||
@@ -335,6 +315,6 @@ ## Pitfalls | ||
1. Currently, Immer only supports plain objects and arrays. PRs are welcome for more language built-in types like `Map` and `Set`. | ||
2. Immer only processes native arrays and plain objects (with a prototype of `null` or `Object`). Any other type of value will be treated verbatim! So if you modify a `Map` or `Buffer` (or whatever complex object from the draft state), the changes will be persisted. But, both in your new and old state! So, in such cases, make sure to always produce fresh instances if you want to keep your state truly immutable. | ||
3. For example, working with `Date` objects is no problem, just make sure you never modify them (by using methods like `setYear` on an existing instance). Instead, always create fresh `Date` instances. Which is probably what you were unconsciously doing already. | ||
4. Since Immer uses proxies, reading huge amounts of data from state comes with an overhead (especially in the ES5 implementation). If this ever becomes an issue (measure before you optimize!), do the current state analysis before entering the producer function or read from the `currentState` rather than the `draftState` | ||
5. Some debuggers (at least Node 6 is known) have trouble debugging when Proxies are in play. Node 8 is known to work correctly. | ||
1. Immer only processes native arrays and plain objects (with a prototype of `null` or `Object`). Any other type of value will be treated verbatim! So if you modify a `Map` or `Buffer` (or whatever complex object from the draft state), the changes will be persisted. But, both in your new and old state! So, in such cases, make sure to always produce fresh instances if you want to keep your state truly immutable. | ||
1. For example, working with `Date` objects is no problem, just make sure you never modify them (by using methods like `setYear` on an existing instance). Instead, always create fresh `Date` instances. Which is probably what you were unconsciously doing already. | ||
1. Since Immer uses proxies, reading huge amounts of data from state comes with an overhead (especially in the ES5 implementation). If this ever becomes an issue (measure before you optimize!), do the current state analysis before entering the producer function or read from the `currentState` rather than the `draftState` | ||
1. Some debuggers (at least Node 6 is known) have trouble debugging when Proxies are in play. Node 8 is known to work correctly. | ||
@@ -347,2 +327,3 @@ ## Cool things built with immer | ||
* [quick-redux](https://github.com/jeffreyyoung/quick-redux) _tools to make redux developement quicker and easier_ | ||
* [react-copy-write](https://github.com/aweary/react-copy-write) _Immutable state with a mutable API_ | ||
@@ -358,47 +339,47 @@ ## How does Immer work? | ||
```javascript | ||
import produce from "immer"; | ||
import produce from "immer" | ||
// object mutations | ||
const todosObj = { | ||
id1: { done: false, body: "Take out the trash" }, | ||
id2: { done: false, body: "Check Email" } | ||
}; | ||
id1: {done: false, body: "Take out the trash"}, | ||
id2: {done: false, body: "Check Email"} | ||
} | ||
// add | ||
const addedTodosObj = produce(todosObj, draft => { | ||
draft["id3"] = { done: false, body: "Buy bananas" }; | ||
}); | ||
draft["id3"] = {done: false, body: "Buy bananas"} | ||
}) | ||
// delete | ||
const deletedTodosObj = produce(todosObj, draft => { | ||
delete draft["id1"]; | ||
}); | ||
delete draft["id1"] | ||
}) | ||
// update | ||
const updatedTodosObj = produce(todosObj, draft => { | ||
draft["id1"].done = true; | ||
}); | ||
draft["id1"].done = true | ||
}) | ||
// array mutations | ||
const todosArray = [ | ||
{ id: "id1", done: false, body: "Take out the trash" }, | ||
{ id: "id2", done: false, body: "Check Email" } | ||
]; | ||
{id: "id1", done: false, body: "Take out the trash"}, | ||
{id: "id2", done: false, body: "Check Email"} | ||
] | ||
// add | ||
const addedTodosArray = produce(todosArray, draft => { | ||
draft.push({ id: "id3", done: false, body: "Buy bananas" }); | ||
}); | ||
draft.push({id: "id3", done: false, body: "Buy bananas"}) | ||
}) | ||
// delete | ||
const deletedTodosArray = produce(todosArray, draft => { | ||
draft.splice(draft.findIndex(todo => todo.id === "id1"), 1); | ||
// or (slower): | ||
// return draft.filter(todo => todo.id !== "id1") | ||
}); | ||
draft.splice(draft.findIndex(todo => todo.id === "id1"), 1) | ||
// or (slower): | ||
// return draft.filter(todo => todo.id !== "id1") | ||
}) | ||
// update | ||
const updatedTodosArray = produce(todosArray, draft => { | ||
draft[draft.findIndex(todo => todo.id === "id1")].done = true; | ||
}); | ||
draft[draft.findIndex(todo => todo.id === "id1")].done = true | ||
}) | ||
``` | ||
@@ -408,8 +389,5 @@ | ||
Here is a [simple benchmark](__performance_tests__/todo.js) on the performance of Immer. | ||
This test takes 100.000 todo items, and updates 10.000 of them. | ||
_Freeze_ indicates that the state tree has been frozen after producing it. This is a _development_ best practice, as it prevents developers from accidentally modifying the state tree. | ||
Here is a [simple benchmark](__performance_tests__/todo.js) on the performance of Immer. This test takes 100.000 todo items, and updates 10.000 of them. _Freeze_ indicates that the state tree has been frozen after producing it. This is a _development_ best practice, as it prevents developers from accidentally modifying the state tree. | ||
These tests were executed on Node 8.4.0. | ||
Use `yarn test:perf` to reproduce them locally. | ||
These tests were executed on Node 8.4.0. Use `yarn test:perf` to reproduce them locally. | ||
@@ -419,2 +397,3 @@ ![performance.png](images/performance.png) | ||
Some observations: | ||
* From `immer` perspective, this benchmark is a _worst case_ scenario, because the root collection it has to proxy is really large relatively to the rest of the data set. | ||
@@ -421,0 +400,0 @@ * The _mutate_, and _deepclone, mutate_ benchmarks establish a baseline on how expensive changing the data is, without immutability (or structural sharing in the deep clone case). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
169147
1016
426