Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

immer

Package Overview
Dependencies
Maintainers
2
Versions
173
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

immer - npm Package Compare versions

Comparing version 4.0.0 to 4.0.1

2

dist/immer.js

@@ -753,3 +753,3 @@ 'use strict';

var configDefaults = {
useProxies: typeof Proxy !== "undefined" && typeof Reflect !== "undefined",
useProxies: typeof Proxy !== "undefined" && typeof Proxy.revocable !== "undefined" && typeof Reflect !== "undefined",
autoFreeze: typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : verifyMinified.name === "verifyMinified",

@@ -756,0 +756,0 @@ onAssign: null,

@@ -749,3 +749,3 @@ var obj;

var configDefaults = {
useProxies: typeof Proxy !== "undefined" && typeof Reflect !== "undefined",
useProxies: typeof Proxy !== "undefined" && typeof Proxy.revocable !== "undefined" && typeof Reflect !== "undefined",
autoFreeze: typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : verifyMinified.name === "verifyMinified",

@@ -752,0 +752,0 @@ onAssign: null,

@@ -1,2 +0,2 @@

!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e=e||self).immer={})}(this,function(e){"use strict";var r,t="undefined"!=typeof Symbol?Symbol("immer-nothing"):((r={})["immer-nothing"]=!0,r),n="undefined"!=typeof Symbol&&Symbol.for?Symbol.for("immer-draftable"):"__$immer_draftable",o="undefined"!=typeof Symbol&&Symbol.for?Symbol.for("immer-state"):"__$immer_state";function i(e){return!!e&&!!e[o]}function a(e){return!!e&&(function(e){if(!e||"object"!=typeof e)return!1;if(Array.isArray(e))return!0;var r=Object.getPrototypeOf(e);return!r||r===Object.prototype}(e)||!!e[n]||!!e.constructor[n])}var f=Object.assign||function(e,r){for(var t in r)l(r,t)&&(e[t]=r[t]);return e},s="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:Object.getOwnPropertyNames;function c(e,r){if(void 0===r&&(r=!1),Array.isArray(e))return e.slice();var t=Object.create(Object.getPrototypeOf(e));return s(e).forEach(function(n){if(n!==o){var i=Object.getOwnPropertyDescriptor(e,n),a=i.value;if(i.get){if(!r)throw new Error("Immer drafts cannot have computed properties");a=i.get.call(e)}i.enumerable?t[n]=a:Object.defineProperty(t,n,{value:a,writable:!0,configurable:!0})}}),t}function u(e,r){if(Array.isArray(e))for(var t=0;t<e.length;t++)r(t,e[t],e);else s(e).forEach(function(t){return r(t,e[t],e)})}function p(e,r){var t=Object.getOwnPropertyDescriptor(e,r);return!!t&&t.enumerable}function l(e,r){return Object.prototype.hasOwnProperty.call(e,r)}function h(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}function d(e){if(!a(e))return e;if(Array.isArray(e))return e.map(d);var r=Object.create(Object.getPrototypeOf(e));for(var t in e)r[t]=d(e[t]);return r}function y(e){if(a(e)&&!i(e)&&!Object.isFrozen(e))if(Object.freeze(e),Array.isArray(e))e.forEach(y);else for(var r in e)y(e[r])}var v=function(e){this.drafts=[],this.parent=e,this.canAutoFreeze=!0,this.patches=null};function b(e){e[o].revoke()}v.prototype.usePatches=function(e){e&&(this.patches=[],this.inversePatches=[],this.patchListener=e)},v.prototype.revoke=function(){this.leave(),this.drafts.forEach(b),this.drafts=null},v.prototype.leave=function(){this===v.current&&(v.current=this.parent)},v.current=null,v.enter=function(){return this.current=new v(this.current)};var g={};function m(e,r){var t=Array.isArray(e),n=A(e);u(n,function(r){!function(e,r,t){var n=g[r];n?n.enumerable=t:g[r]=n={configurable:!0,enumerable:t,get:function(){return function(e,r){E(e);var t=O(w(e),r);if(e.finalizing)return t;if(t===O(e.base,r)&&a(t))return j(e),e.copy[r]=m(t,e);return t}(this[o],r)},set:function(e){!function(e,r,t){if(E(e),e.assigned[r]=!0,!e.modified){if(h(t,O(w(e),r)))return;z(e),j(e)}e.copy[r]=t}(this[o],r,e)}};Object.defineProperty(e,r,n)}(n,r,t||p(e,r))});var i,f,s,c=r?r.scope:v.current;return i=n,f=o,s={scope:c,modified:!1,finalizing:!1,finalized:!1,assigned:{},parent:r,base:e,draft:n,copy:null,revoke:P,revoked:!1},Object.defineProperty(i,f,{value:s,enumerable:!1,writable:!0}),c.drafts.push(n),n}function P(){this.revoked=!0}function w(e){return e.copy||e.base}function O(e,r){var t=e[o];if(t&&!t.finalizing){t.finalizing=!0;var n=e[r];return t.finalizing=!1,n}return e[r]}function z(e){e.modified||(e.modified=!0,e.parent&&z(e.parent))}function j(e){e.copy||(e.copy=A(e.base))}function A(e){var r=e&&e[o];if(r){r.finalizing=!0;var t=c(r.draft,!0);return r.finalizing=!1,t}return c(e)}function E(e){if(!0===e.revoked)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(w(e)))}function D(e){for(var r=e.length-1;r>=0;r--){var t=e[r][o];t.modified||(Array.isArray(t.base)?k(t)&&z(t):F(t)&&z(t))}}function F(e){for(var r=e.base,t=e.draft,n=Object.keys(t),i=n.length-1;i>=0;i--){var a=n[i],f=r[a];if(void 0===f&&!l(r,a))return!0;var s=t[a],c=s&&s[o];if(c?c.base!==f:!h(s,f))return!0}return n.length!==Object.keys(r).length}function k(e){var r=e.draft;if(r.length!==e.base.length)return!0;var t=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!t||t.get)}var x=Object.freeze({willFinalize:function(e,r,t){e.drafts.forEach(function(e){e[o].finalizing=!0}),t?i(r)&&r[o].scope===e&&D(e.drafts):(e.patches&&function e(r){if(r&&"object"==typeof r){var t=r[o];if(t){var n=t.base,i=t.draft,a=t.assigned;if(Array.isArray(r)){if(k(t)){if(z(t),a.length=!0,i.length<n.length)for(var f=i.length;f<n.length;f++)a[f]=!1;else for(var s=n.length;s<i.length;s++)a[s]=!0;for(var c=0;c<i.length;c++)void 0===a[c]&&e(i[c])}}else Object.keys(i).forEach(function(r){void 0!==n[r]||l(n,r)?a[r]||e(i[r]):(a[r]=!0,z(t))}),Object.keys(n).forEach(function(e){void 0!==i[e]||l(i,e)||(a[e]=!1,z(t))})}}}(e.drafts[0]),D(e.drafts))},createProxy:m});function R(e,r){var t=r?r.scope:v.current,n={scope:t,modified:!1,finalized:!1,assigned:{},parent:r,base:e,draft:null,drafts:{},copy:null,revoke:null},o=Array.isArray(e)?Proxy.revocable([n],I):Proxy.revocable(n,S),i=o.revoke,a=o.proxy;return n.draft=a,n.revoke=i,t.drafts.push(a),a}var S={get:function(e,r){if(r===o)return e;var t=e.drafts;if(!e.modified&&l(t,r))return t[r];var n=N(e)[r];if(e.finalized||!a(n))return n;if(e.modified){if(n!==_(e.base,r))return n;t=e.copy}return t[r]=R(n,e)},has:function(e,r){return r in N(e)},ownKeys:function(e){return Reflect.ownKeys(N(e))},set:function(e,r,t){if(!e.modified){var n=_(e.base,r),o=t?h(n,t)||t===e.drafts[r]:h(n,t)&&r in e.base;if(o)return!0;T(e)}return e.assigned[r]=!0,e.copy[r]=t,!0},deleteProperty:function(e,r){void 0!==_(e.base,r)||r in e.base?(e.assigned[r]=!1,T(e)):e.assigned[r]&&delete e.assigned[r];e.copy&&delete e.copy[r];return!0},getOwnPropertyDescriptor:function(e,r){var t=N(e),n=Reflect.getOwnPropertyDescriptor(t,r);n&&(n.writable=!0,n.configurable=!Array.isArray(t)||"length"!==r);return n},defineProperty:function(){throw new Error("Object.defineProperty() cannot be used on an Immer draft")},getPrototypeOf:function(e){return Object.getPrototypeOf(e.base)},setPrototypeOf:function(){throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft")}},I={};function N(e){return e.copy||e.base}function _(e,r){var t=e[o],n=Reflect.getOwnPropertyDescriptor(t?N(t):e,r);return n&&n.value}function T(e){e.modified||(e.modified=!0,e.copy=f(c(e.base),e.drafts),e.drafts=null,e.parent&&T(e.parent))}u(S,function(e,r){I[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}}),I.deleteProperty=function(e,r){if(isNaN(parseInt(r)))throw new Error("Immer only supports deleting array indices");return S.deleteProperty.call(this,e[0],r)},I.set=function(e,r,t){if("length"!==r&&isNaN(parseInt(r)))throw new Error("Immer only supports setting array indices and the 'length' property");return S.set.call(this,e[0],r,t)};var C=Object.freeze({willFinalize:function(){},createProxy:R});function U(e,r,t,n){Array.isArray(e.base)?function(e,r,t,n){var o,i,a=e.base,f=e.copy,s=e.assigned;f.length<a.length&&(a=(o=[f,a])[0],f=o[1],t=(i=[n,t])[0],n=i[1]);var c=f.length-a.length,u=0;for(;a[u]===f[u]&&u<a.length;)++u;var p=a.length;for(;p>u&&a[p-1]===f[p+c-1];)--p;for(var l=u;l<p;++l)if(s[l]&&f[l]!==a[l]){var h=r.concat([l]);t.push({op:"replace",path:h,value:f[l]}),n.push({op:"replace",path:h,value:a[l]})}for(var d=t.length,y=p+c-1;y>=p;--y){var v=r.concat([y]);t[d+y-p]={op:"add",path:v,value:f[y]},n.push({op:"remove",path:v})}}(e,r,t,n):function(e,r,t,n){var o=e.base,i=e.copy;u(e.assigned,function(e,a){var f=o[e],s=i[e],c=a?e in o?"replace":"add":"remove";if(f!==s||"replace"!==c){var u=r.concat(e);t.push("remove"===c?{op:c,path:u}:{op:c,path:u,value:s}),n.push("add"===c?{op:"remove",path:u}:"remove"===c?{op:"add",path:u,value:f}:{op:"replace",path:u,value:f})}})}(e,r,t,n)}var W=function(e,r){for(var t=0,n=r;t<n.length;t+=1){var o=n[t],i=o.path,a=o.op,f=d(o.value);if(!i.length)throw new Error("Illegal state");for(var s=e,c=0;c<i.length-1;c++)if(!(s=s[i[c]])||"object"!=typeof s)throw new Error("Cannot apply patch, path doesn't resolve: "+i.join("/"));var u=i[i.length-1];switch(a){case"replace":s[u]=f;break;case"add":Array.isArray(s)?s.splice(u,0,f):s[u]=f;break;case"remove":Array.isArray(s)?s.splice(u,1):delete s[u];break;default:throw new Error("Unsupported patch operation: "+a)}}return e};var K={useProxies:"undefined"!=typeof Proxy&&"undefined"!=typeof Reflect,autoFreeze:"undefined"!=typeof process?"production"!==process.env.NODE_ENV:"verifyMinified"===function(){}.name,onAssign:null,onDelete:null,onCopy:null},M=function(e){f(this,K,e),this.setUseProxies(this.useProxies),this.produce=this.produce.bind(this)};M.prototype.produce=function(e,r,n){var o,i=this;if("function"==typeof e&&"function"!=typeof r){var f=r;r=e;var s=this;return function(e){var t=this;void 0===e&&(e=f);for(var n=[],o=arguments.length-1;o-- >0;)n[o]=arguments[o+1];return s.produce(e,function(e){return r.call.apply(r,[t,e].concat(n))})}}if("function"!=typeof r)throw new Error("The first or second argument to `produce` must be a function");if(void 0!==n&&"function"!=typeof n)throw new Error("The third argument to `produce` must be a function or undefined");if(a(e)){var c=v.enter(),u=this.createProxy(e),p=!0;try{o=r(u),p=!1}finally{p?c.revoke():c.leave()}return o instanceof Promise?o.then(function(e){return c.usePatches(n),i.processResult(e,c)},function(e){throw c.revoke(),e}):(c.usePatches(n),this.processResult(o,c))}if((o=r(e))!==t)return void 0===o&&(o=e),this.maybeFreeze(o,!0),o},M.prototype.produceWithPatches=function(e,r,t){var n,o,i=this;if("function"==typeof e)return function(r){for(var t=[],n=arguments.length-1;n-- >0;)t[n]=arguments[n+1];return i.produceWithPatches(r,function(r){return e.apply(void 0,[r].concat(t))})};if(t)throw new Error("A patch listener cannot be passed to produceWithPatches");return[this.produce(e,r,function(e,r){n=e,o=r}),n,o]},M.prototype.createDraft=function(e){if(!a(e))throw new Error("First argument to `createDraft` must be a plain object, an array, or an immerable object");var r=v.enter(),t=this.createProxy(e);return t[o].isManual=!0,r.leave(),t},M.prototype.finishDraft=function(e,r){var t=e&&e[o];if(!t||!t.isManual)throw new Error("First argument to `finishDraft` must be a draft returned by `createDraft`");if(t.finalized)throw new Error("The given draft is already finalized");var n=t.scope;return n.usePatches(r),this.processResult(void 0,n)},M.prototype.setAutoFreeze=function(e){this.autoFreeze=e},M.prototype.setUseProxies=function(e){this.useProxies=e,f(this,e?C:x)},M.prototype.applyPatches=function(e,r){var t;for(t=r.length-1;t>=0;t--){var n=r[t];if(0===n.path.length&&"replace"===n.op){e=n.value;break}}return i(e)?W(e,r):this.produce(e,function(e){return W(e,r.slice(t+1))})},M.prototype.processResult=function(e,r){var n=r.drafts[0],i=void 0!==e&&e!==n;if(this.willFinalize(r,e,i),i){if(n[o].modified)throw r.revoke(),new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.");a(e)&&(e=this.finalize(e,null,r),this.maybeFreeze(e)),r.patches&&(r.patches.push({op:"replace",path:[],value:e}),r.inversePatches.push({op:"replace",path:[],value:n[o].base}))}else e=this.finalize(n,[],r);return r.revoke(),r.patches&&r.patchListener(r.patches,r.inversePatches),e!==t?e:void 0},M.prototype.finalize=function(e,r,t){var n=this,i=e[o];if(!i)return Object.isFrozen(e)?e:this.finalizeTree(e,null,t);if(i.scope!==t)return e;if(!i.modified)return this.maybeFreeze(i.base,!0),i.base;if(!i.finalized){if(i.finalized=!0,this.finalizeTree(i.draft,r,t),this.onDelete)if(this.useProxies){var a=i.assigned;for(var f in a)a[f]||this.onDelete(i,f)}else{var s=i.base,c=i.copy;u(s,function(e){l(c,e)||n.onDelete(i,e)})}this.onCopy&&this.onCopy(i),this.autoFreeze&&t.canAutoFreeze&&Object.freeze(i.copy),r&&t.patches&&U(i,r,t.patches,t.inversePatches)}return i.copy},M.prototype.finalizeTree=function(e,r,t){var n=this,f=e[o];f&&(this.useProxies||(f.copy=c(f.draft,!0)),e=f.copy);var s=!!r&&!!t.patches,l=function(o,c,d){if(c===d)throw Error("Immer forbids circular references");var y=!!f&&d===e;if(i(c)){var v=y&&s&&!f.assigned[o]?r.concat(o):null;if(i(c=n.finalize(c,v,t))&&(t.canAutoFreeze=!1),Array.isArray(d)||p(d,o)?d[o]=c:Object.defineProperty(d,o,{value:c}),y&&c===f.base[o])return}else{if(y&&h(c,f.base[o]))return;a(c)&&!Object.isFrozen(c)&&(u(c,l),n.maybeFreeze(c))}y&&n.onAssign&&n.onAssign(f,o,c)};return u(e,l),e},M.prototype.maybeFreeze=function(e,r){void 0===r&&(r=!1),this.autoFreeze&&!i(e)&&(r?y(e):Object.freeze(e))};var L=new M,$=L.produce,J=L.produceWithPatches.bind(L),V=L.setAutoFreeze.bind(L),q=L.setUseProxies.bind(L),B=L.applyPatches.bind(L),G=L.createDraft.bind(L),H=L.finishDraft.bind(L);e.Immer=M,e.applyPatches=B,e.createDraft=G,e.default=$,e.finishDraft=H,e.immerable=n,e.isDraft=i,e.isDraftable=a,e.nothing=t,e.original=function(e){if(e&&e[o])return e[o].base},e.produce=$,e.produceWithPatches=J,e.setAutoFreeze=V,e.setUseProxies=q,Object.defineProperty(e,"__esModule",{value:!0})});
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e=e||self).immer={})}(this,function(e){"use strict";var r,t="undefined"!=typeof Symbol?Symbol("immer-nothing"):((r={})["immer-nothing"]=!0,r),n="undefined"!=typeof Symbol&&Symbol.for?Symbol.for("immer-draftable"):"__$immer_draftable",o="undefined"!=typeof Symbol&&Symbol.for?Symbol.for("immer-state"):"__$immer_state";function i(e){return!!e&&!!e[o]}function a(e){return!!e&&(function(e){if(!e||"object"!=typeof e)return!1;if(Array.isArray(e))return!0;var r=Object.getPrototypeOf(e);return!r||r===Object.prototype}(e)||!!e[n]||!!e.constructor[n])}var f=Object.assign||function(e,r){for(var t in r)l(r,t)&&(e[t]=r[t]);return e},s="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:Object.getOwnPropertyNames;function c(e,r){if(void 0===r&&(r=!1),Array.isArray(e))return e.slice();var t=Object.create(Object.getPrototypeOf(e));return s(e).forEach(function(n){if(n!==o){var i=Object.getOwnPropertyDescriptor(e,n),a=i.value;if(i.get){if(!r)throw new Error("Immer drafts cannot have computed properties");a=i.get.call(e)}i.enumerable?t[n]=a:Object.defineProperty(t,n,{value:a,writable:!0,configurable:!0})}}),t}function u(e,r){if(Array.isArray(e))for(var t=0;t<e.length;t++)r(t,e[t],e);else s(e).forEach(function(t){return r(t,e[t],e)})}function p(e,r){var t=Object.getOwnPropertyDescriptor(e,r);return!!t&&t.enumerable}function l(e,r){return Object.prototype.hasOwnProperty.call(e,r)}function h(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}function d(e){if(!a(e))return e;if(Array.isArray(e))return e.map(d);var r=Object.create(Object.getPrototypeOf(e));for(var t in e)r[t]=d(e[t]);return r}function y(e){if(a(e)&&!i(e)&&!Object.isFrozen(e))if(Object.freeze(e),Array.isArray(e))e.forEach(y);else for(var r in e)y(e[r])}var v=function(e){this.drafts=[],this.parent=e,this.canAutoFreeze=!0,this.patches=null};function b(e){e[o].revoke()}v.prototype.usePatches=function(e){e&&(this.patches=[],this.inversePatches=[],this.patchListener=e)},v.prototype.revoke=function(){this.leave(),this.drafts.forEach(b),this.drafts=null},v.prototype.leave=function(){this===v.current&&(v.current=this.parent)},v.current=null,v.enter=function(){return this.current=new v(this.current)};var g={};function m(e,r){var t=Array.isArray(e),n=A(e);u(n,function(r){!function(e,r,t){var n=g[r];n?n.enumerable=t:g[r]=n={configurable:!0,enumerable:t,get:function(){return function(e,r){E(e);var t=O(w(e),r);if(e.finalizing)return t;if(t===O(e.base,r)&&a(t))return j(e),e.copy[r]=m(t,e);return t}(this[o],r)},set:function(e){!function(e,r,t){if(E(e),e.assigned[r]=!0,!e.modified){if(h(t,O(w(e),r)))return;z(e),j(e)}e.copy[r]=t}(this[o],r,e)}};Object.defineProperty(e,r,n)}(n,r,t||p(e,r))});var i,f,s,c=r?r.scope:v.current;return i=n,f=o,s={scope:c,modified:!1,finalizing:!1,finalized:!1,assigned:{},parent:r,base:e,draft:n,copy:null,revoke:P,revoked:!1},Object.defineProperty(i,f,{value:s,enumerable:!1,writable:!0}),c.drafts.push(n),n}function P(){this.revoked=!0}function w(e){return e.copy||e.base}function O(e,r){var t=e[o];if(t&&!t.finalizing){t.finalizing=!0;var n=e[r];return t.finalizing=!1,n}return e[r]}function z(e){e.modified||(e.modified=!0,e.parent&&z(e.parent))}function j(e){e.copy||(e.copy=A(e.base))}function A(e){var r=e&&e[o];if(r){r.finalizing=!0;var t=c(r.draft,!0);return r.finalizing=!1,t}return c(e)}function E(e){if(!0===e.revoked)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(w(e)))}function D(e){for(var r=e.length-1;r>=0;r--){var t=e[r][o];t.modified||(Array.isArray(t.base)?k(t)&&z(t):F(t)&&z(t))}}function F(e){for(var r=e.base,t=e.draft,n=Object.keys(t),i=n.length-1;i>=0;i--){var a=n[i],f=r[a];if(void 0===f&&!l(r,a))return!0;var s=t[a],c=s&&s[o];if(c?c.base!==f:!h(s,f))return!0}return n.length!==Object.keys(r).length}function k(e){var r=e.draft;if(r.length!==e.base.length)return!0;var t=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!t||t.get)}var x=Object.freeze({willFinalize:function(e,r,t){e.drafts.forEach(function(e){e[o].finalizing=!0}),t?i(r)&&r[o].scope===e&&D(e.drafts):(e.patches&&function e(r){if(r&&"object"==typeof r){var t=r[o];if(t){var n=t.base,i=t.draft,a=t.assigned;if(Array.isArray(r)){if(k(t)){if(z(t),a.length=!0,i.length<n.length)for(var f=i.length;f<n.length;f++)a[f]=!1;else for(var s=n.length;s<i.length;s++)a[s]=!0;for(var c=0;c<i.length;c++)void 0===a[c]&&e(i[c])}}else Object.keys(i).forEach(function(r){void 0!==n[r]||l(n,r)?a[r]||e(i[r]):(a[r]=!0,z(t))}),Object.keys(n).forEach(function(e){void 0!==i[e]||l(i,e)||(a[e]=!1,z(t))})}}}(e.drafts[0]),D(e.drafts))},createProxy:m});function R(e,r){var t=r?r.scope:v.current,n={scope:t,modified:!1,finalized:!1,assigned:{},parent:r,base:e,draft:null,drafts:{},copy:null,revoke:null},o=Array.isArray(e)?Proxy.revocable([n],I):Proxy.revocable(n,S),i=o.revoke,a=o.proxy;return n.draft=a,n.revoke=i,t.drafts.push(a),a}var S={get:function(e,r){if(r===o)return e;var t=e.drafts;if(!e.modified&&l(t,r))return t[r];var n=N(e)[r];if(e.finalized||!a(n))return n;if(e.modified){if(n!==_(e.base,r))return n;t=e.copy}return t[r]=R(n,e)},has:function(e,r){return r in N(e)},ownKeys:function(e){return Reflect.ownKeys(N(e))},set:function(e,r,t){if(!e.modified){var n=_(e.base,r),o=t?h(n,t)||t===e.drafts[r]:h(n,t)&&r in e.base;if(o)return!0;T(e)}return e.assigned[r]=!0,e.copy[r]=t,!0},deleteProperty:function(e,r){void 0!==_(e.base,r)||r in e.base?(e.assigned[r]=!1,T(e)):e.assigned[r]&&delete e.assigned[r];e.copy&&delete e.copy[r];return!0},getOwnPropertyDescriptor:function(e,r){var t=N(e),n=Reflect.getOwnPropertyDescriptor(t,r);n&&(n.writable=!0,n.configurable=!Array.isArray(t)||"length"!==r);return n},defineProperty:function(){throw new Error("Object.defineProperty() cannot be used on an Immer draft")},getPrototypeOf:function(e){return Object.getPrototypeOf(e.base)},setPrototypeOf:function(){throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft")}},I={};function N(e){return e.copy||e.base}function _(e,r){var t=e[o],n=Reflect.getOwnPropertyDescriptor(t?N(t):e,r);return n&&n.value}function T(e){e.modified||(e.modified=!0,e.copy=f(c(e.base),e.drafts),e.drafts=null,e.parent&&T(e.parent))}u(S,function(e,r){I[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}}),I.deleteProperty=function(e,r){if(isNaN(parseInt(r)))throw new Error("Immer only supports deleting array indices");return S.deleteProperty.call(this,e[0],r)},I.set=function(e,r,t){if("length"!==r&&isNaN(parseInt(r)))throw new Error("Immer only supports setting array indices and the 'length' property");return S.set.call(this,e[0],r,t)};var C=Object.freeze({willFinalize:function(){},createProxy:R});function U(e,r,t,n){Array.isArray(e.base)?function(e,r,t,n){var o,i,a=e.base,f=e.copy,s=e.assigned;f.length<a.length&&(a=(o=[f,a])[0],f=o[1],t=(i=[n,t])[0],n=i[1]);var c=f.length-a.length,u=0;for(;a[u]===f[u]&&u<a.length;)++u;var p=a.length;for(;p>u&&a[p-1]===f[p+c-1];)--p;for(var l=u;l<p;++l)if(s[l]&&f[l]!==a[l]){var h=r.concat([l]);t.push({op:"replace",path:h,value:f[l]}),n.push({op:"replace",path:h,value:a[l]})}for(var d=t.length,y=p+c-1;y>=p;--y){var v=r.concat([y]);t[d+y-p]={op:"add",path:v,value:f[y]},n.push({op:"remove",path:v})}}(e,r,t,n):function(e,r,t,n){var o=e.base,i=e.copy;u(e.assigned,function(e,a){var f=o[e],s=i[e],c=a?e in o?"replace":"add":"remove";if(f!==s||"replace"!==c){var u=r.concat(e);t.push("remove"===c?{op:c,path:u}:{op:c,path:u,value:s}),n.push("add"===c?{op:"remove",path:u}:"remove"===c?{op:"add",path:u,value:f}:{op:"replace",path:u,value:f})}})}(e,r,t,n)}var W=function(e,r){for(var t=0,n=r;t<n.length;t+=1){var o=n[t],i=o.path,a=o.op,f=d(o.value);if(!i.length)throw new Error("Illegal state");for(var s=e,c=0;c<i.length-1;c++)if(!(s=s[i[c]])||"object"!=typeof s)throw new Error("Cannot apply patch, path doesn't resolve: "+i.join("/"));var u=i[i.length-1];switch(a){case"replace":s[u]=f;break;case"add":Array.isArray(s)?s.splice(u,0,f):s[u]=f;break;case"remove":Array.isArray(s)?s.splice(u,1):delete s[u];break;default:throw new Error("Unsupported patch operation: "+a)}}return e};var K={useProxies:"undefined"!=typeof Proxy&&void 0!==Proxy.revocable&&"undefined"!=typeof Reflect,autoFreeze:"undefined"!=typeof process?"production"!==process.env.NODE_ENV:"verifyMinified"===function(){}.name,onAssign:null,onDelete:null,onCopy:null},M=function(e){f(this,K,e),this.setUseProxies(this.useProxies),this.produce=this.produce.bind(this)};M.prototype.produce=function(e,r,n){var o,i=this;if("function"==typeof e&&"function"!=typeof r){var f=r;r=e;var s=this;return function(e){var t=this;void 0===e&&(e=f);for(var n=[],o=arguments.length-1;o-- >0;)n[o]=arguments[o+1];return s.produce(e,function(e){return r.call.apply(r,[t,e].concat(n))})}}if("function"!=typeof r)throw new Error("The first or second argument to `produce` must be a function");if(void 0!==n&&"function"!=typeof n)throw new Error("The third argument to `produce` must be a function or undefined");if(a(e)){var c=v.enter(),u=this.createProxy(e),p=!0;try{o=r(u),p=!1}finally{p?c.revoke():c.leave()}return o instanceof Promise?o.then(function(e){return c.usePatches(n),i.processResult(e,c)},function(e){throw c.revoke(),e}):(c.usePatches(n),this.processResult(o,c))}if((o=r(e))!==t)return void 0===o&&(o=e),this.maybeFreeze(o,!0),o},M.prototype.produceWithPatches=function(e,r,t){var n,o,i=this;if("function"==typeof e)return function(r){for(var t=[],n=arguments.length-1;n-- >0;)t[n]=arguments[n+1];return i.produceWithPatches(r,function(r){return e.apply(void 0,[r].concat(t))})};if(t)throw new Error("A patch listener cannot be passed to produceWithPatches");return[this.produce(e,r,function(e,r){n=e,o=r}),n,o]},M.prototype.createDraft=function(e){if(!a(e))throw new Error("First argument to `createDraft` must be a plain object, an array, or an immerable object");var r=v.enter(),t=this.createProxy(e);return t[o].isManual=!0,r.leave(),t},M.prototype.finishDraft=function(e,r){var t=e&&e[o];if(!t||!t.isManual)throw new Error("First argument to `finishDraft` must be a draft returned by `createDraft`");if(t.finalized)throw new Error("The given draft is already finalized");var n=t.scope;return n.usePatches(r),this.processResult(void 0,n)},M.prototype.setAutoFreeze=function(e){this.autoFreeze=e},M.prototype.setUseProxies=function(e){this.useProxies=e,f(this,e?C:x)},M.prototype.applyPatches=function(e,r){var t;for(t=r.length-1;t>=0;t--){var n=r[t];if(0===n.path.length&&"replace"===n.op){e=n.value;break}}return i(e)?W(e,r):this.produce(e,function(e){return W(e,r.slice(t+1))})},M.prototype.processResult=function(e,r){var n=r.drafts[0],i=void 0!==e&&e!==n;if(this.willFinalize(r,e,i),i){if(n[o].modified)throw r.revoke(),new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.");a(e)&&(e=this.finalize(e,null,r),this.maybeFreeze(e)),r.patches&&(r.patches.push({op:"replace",path:[],value:e}),r.inversePatches.push({op:"replace",path:[],value:n[o].base}))}else e=this.finalize(n,[],r);return r.revoke(),r.patches&&r.patchListener(r.patches,r.inversePatches),e!==t?e:void 0},M.prototype.finalize=function(e,r,t){var n=this,i=e[o];if(!i)return Object.isFrozen(e)?e:this.finalizeTree(e,null,t);if(i.scope!==t)return e;if(!i.modified)return this.maybeFreeze(i.base,!0),i.base;if(!i.finalized){if(i.finalized=!0,this.finalizeTree(i.draft,r,t),this.onDelete)if(this.useProxies){var a=i.assigned;for(var f in a)a[f]||this.onDelete(i,f)}else{var s=i.base,c=i.copy;u(s,function(e){l(c,e)||n.onDelete(i,e)})}this.onCopy&&this.onCopy(i),this.autoFreeze&&t.canAutoFreeze&&Object.freeze(i.copy),r&&t.patches&&U(i,r,t.patches,t.inversePatches)}return i.copy},M.prototype.finalizeTree=function(e,r,t){var n=this,f=e[o];f&&(this.useProxies||(f.copy=c(f.draft,!0)),e=f.copy);var s=!!r&&!!t.patches,l=function(o,c,d){if(c===d)throw Error("Immer forbids circular references");var y=!!f&&d===e;if(i(c)){var v=y&&s&&!f.assigned[o]?r.concat(o):null;if(i(c=n.finalize(c,v,t))&&(t.canAutoFreeze=!1),Array.isArray(d)||p(d,o)?d[o]=c:Object.defineProperty(d,o,{value:c}),y&&c===f.base[o])return}else{if(y&&h(c,f.base[o]))return;a(c)&&!Object.isFrozen(c)&&(u(c,l),n.maybeFreeze(c))}y&&n.onAssign&&n.onAssign(f,o,c)};return u(e,l),e},M.prototype.maybeFreeze=function(e,r){void 0===r&&(r=!1),this.autoFreeze&&!i(e)&&(r?y(e):Object.freeze(e))};var L=new M,$=L.produce,J=L.produceWithPatches.bind(L),V=L.setAutoFreeze.bind(L),q=L.setUseProxies.bind(L),B=L.applyPatches.bind(L),G=L.createDraft.bind(L),H=L.finishDraft.bind(L);e.Immer=M,e.applyPatches=B,e.createDraft=G,e.default=$,e.finishDraft=H,e.immerable=n,e.isDraft=i,e.isDraftable=a,e.nothing=t,e.original=function(e){if(e&&e[o])return e[o].base},e.produce=$,e.produceWithPatches=J,e.setAutoFreeze=V,e.setUseProxies=q,Object.defineProperty(e,"__esModule",{value:!0})});
//# sourceMappingURL=immer.umd.js.map
{
"name": "immer",
"version": "4.0.0",
"version": "4.0.1",
"description": "Create your next immutable state by mutating the current one",

@@ -21,3 +21,5 @@ "main": "dist/immer.js",

"build": "rimraf dist/ && yarn-or-npm bili && yarn-or-npm typed",
"typed": "cpx 'src/immer.{d.ts,js.flow}' dist -v"
"typed": "cpx 'src/immer.{d.ts,js.flow}' dist -v",
"publish-docs": "cd website && GIT_USER=mweststrate USE_SSH=true yarn run publish-gh-pages",
"start": "cd website && yarn start"
},

@@ -24,0 +26,0 @@ "husky": {

@@ -5,3 +5,3 @@ <img src="images/immer-logo.png" height="200px" align="right"/>

[![npm](https://img.shields.io/npm/v/immer.svg)](https://www.npmjs.com/package/immer) [![Build Status](https://travis-ci.org/immerjs/immer.svg?branch=master)](https://travis-ci.org/immerjs/immer) [![Coverage Status](https://coveralls.io/repos/github/immerjs/immer/badge.svg?branch=master)](https://coveralls.io/github/immerjs/immer?branch=master) [![Minzipped size](https://img.shields.io/bundlephobia/minzip/immer.svg)](https://bundlephobia.com/result?p=immer) [![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) [![Build Status](https://travis-ci.org/immerjs/immer.svg?branch=master)](https://travis-ci.org/immerjs/immer) [![Coverage Status](https://coveralls.io/repos/github/mweststrate/immer/badge.svg?branch=master)](https://coveralls.io/github/mweststrate/immer?branch=master) [![Minzipped size](https://img.shields.io/bundlephobia/minzip/immer.svg)](https://bundlephobia.com/result?p=immer) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![OpenCollective](https://opencollective.com/immer/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/immer/sponsors/badge.svg)](#sponsors)

@@ -12,826 +12,12 @@ _Create the next immutable state tree by simply modifying the current tree_

### [Release notes](https://github.com/immerjs/immer/releases)
## Documentation
Did Immer make a difference to your project? Consider buying me a coffee!<br/><a href="https://www.buymeacoffee.com/mweststrate" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: auto !important;width: auto !important;" ></a>
The documentation of this package is hosted at https://immerjs.github.io/immer/
---
## Support
- NPM: `npm install immer`
- Yarn: `yarn add immer`
- 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>`
Did Immer make a difference to your project? Join the open collective at https://opencollective.com/immer!
---
## Release notes
- Egghead lesson covering all of immer (7m): [Simplify creating immutable data trees with Immer](https://egghead.io/lessons/redux-simplify-creating-immutable-data-trees-with-immer)
- Introduction blogpost: [Immer: Immutability the easy way](https://medium.com/@mweststrate/introducing-immer-immutability-the-easy-way-9d73d8f71cb3)
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 temporary _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.
![immer-hd.png](images/hd/immer.png)
Using Immer is like having a personal assistant; he takes a letter (the current state) and gives you a copy (draft) to jot changes onto. Once you are done, the assistant will take your draft and produce the real immutable, final letter for you (the next state).
A mindful reader might notice that this is quite similar to `withMutations` of ImmutableJS. It is indeed, but generalized and applied to plain, native JavaScript data structures (arrays and objects) without further needing any library.
## External resources
- Blog: [The Rise of Immer in React](https://www.netlify.com/blog/2018/09/12/the-rise-of-immer-in-react/)
- Blog: by Workday Prism on why they picked Immer to manage immutable state [The Search for a Strongly-Typed, Immutable State](https://medium.com/workday-engineering/workday-prism-analytics-the-search-for-a-strongly-typed-immutable-state-a09f6768b2b5)
- Blog: [Immutability in React and Redux: The Complete Guide](https://daveceddia.com/react-redux-immutability-guide/)
- Video tutorial: [Using Immer with React.setState](https://codedaily.io/screencasts/86/Immutable-Data-with-Immer-and-React-setState)
- [Talk](https://www.youtube.com/watch?v=-gJbS7YjcSo) + [slides](http://immer.surge.sh/) on Immer at React Finland 2018 by Michel Weststrate
- [ForwardJS 2019: Immutability is Changing - From Immutable.js to Immer](https://www.youtube.com/watch?v=bFuRvcAEiHg&feature=youtu.be) by [shawn swyx wang](https://twitter.com/swyx/)
## API
The Immer package exposes a default function that does all the work.
`produce(currentState, producer: (draftState) => void): nextState`
There is also a curried overload that is explained [below](#currying).
## Example
```javascript
import produce from "immer"
const baseState = [
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
]
const nextState = produce(baseState, draftState => {
draftState.push({todo: "Tweet about it"})
draftState[1].done = true
})
```
The interesting thing about Immer is that the `baseState` will be untouched, but the `nextState` will reflect all changes made to `draftState`.
```javascript
// the new item is only added to the next state,
// base state is unmodified
expect(baseState.length).toBe(2)
expect(nextState.length).toBe(3)
// same for the changed 'done' prop
expect(baseState[1].done).toBe(false)
expect(nextState[1].done).toBe(true)
// unchanged data is structurally shared
expect(nextState[0]).toBe(baseState[0])
// changed data not (dûh)
expect(nextState[1]).not.toBe(baseState[1])
```
## Benefits
- Immutability with normal JavaScript objects and arrays. No new APIs to learn!
- Strongly typed, no string based paths selectors etc.
- Structural sharing out of the box
- Object freezing out of the box
- Deep updates are a breeze
- Boilerplate reduction. Less noise, more concise code.
- Small [![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)
Read further to see all these benefits explained.
## Reducer Example
Here is a simple example of the difference that Immer could make in practice.
```javascript
// Redux reducer
// Shortened, based on: https://github.com/reactjs/redux/blob/master/examples/shopping-cart/src/reducers/products.js
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
}
}
```
After using Immer, that simply becomes:
```javascript
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
})
}
})
```
Notice that it is not needed to handle the default case, a producer that doesn't do anything will simply return the original state.
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).
_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._
## React.setState example
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)):
```javascript
/**
* Classic React.setState with a deep merge
*/
onBirthDayClick1 = () => {
this.setState(prevState => ({
user: {
...prevState.user,
age: prevState.user.age + 1
}
}))
}
/**
* ...But, since setState accepts functions,
* we can just create a curried producer and further simplify!
*/
onBirthDayClick2 = () => {
this.setState(
produce(draft => {
draft.user.age += 1
})
)
}
```
## Currying
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.
For example:
```javascript
// mapper will be of signature (state, index) => state
const mapper = produce((draft, index) => {
draft.index = index
})
// example usage
console.dir([{}, {}, {}].map(mapper))
//[{index: 0}, {index: 1}, {index: 2}])
```
This mechanism can also nicely be leveraged to further simplify our example reducer:
```javascript
import produce from "immer"
const byId = produce((draft, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
return
}
})
```
Note that `state` is now factored out (the created reducer will accept a state, and invoke the bound producer with it).
If you want to initialize an uninitialized state using this construction, you can do so by passing the initial state as second argument to `produce`:
```javascript
import produce from "immer"
const byId = produce(
(draft, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
return
}
},
{
1: {id: 1, name: "product-1"}
}
)
```
##### Fun with currying
A random fun example just for inspiration: a neat trick is to turn `Object.assign` into a producer to create a "spread" function that is smarter than the normal spread operator, as it doesn't produce a new state if the result doesn't actually change ([details & explanation](https://twitter.com/mweststrate/status/1045059430256119809)). Quick example:
```javascript
import produce from "immer"
const spread = produce(Object.assign)
const base = {x: 1, y: 1}
console.log({...base, y: 1} === base) // false
console.log(spread(base, {y: 1}) === base) // true! base is recycled as no actual new value was produced
console.log(spread(base, {y: 2}) === base) // false, produced a new object as it should
```
## Patches
During the run of a producer, Immer can record all the patches that would replay the changes made by the reducer. This is a very powerful tool if you want to fork your state temporarily and replay the changes to the original.
Patches are useful in few scenarios:
- To exchange incremental updates with other parties, for example over websockets
- For debugging / traces, to see precisely how state is changed over time
- As basis for undo/redo or as an approach to replay changes on a slightly different state tree
To help with replaying patches, `applyPatches` comes in handy. Here is an example how patches could be used to record the incremental updates and (inverse) apply them:
```javascript
import produce, {applyPatches} from "immer"
let state = {
name: "Micheal",
age: 32
}
// Let's assume the user is in a wizard, and we don't know whether
// his changes should end up in the base state ultimately or not...
let fork = state
// all the changes the user made in the wizard
let changes = []
// the inverse of all the changes made in the wizard
let inverseChanges = []
fork = produce(
fork,
draft => {
draft.age = 33
},
// The third argument to produce is a callback to which the patches will be fed
(patches, inversePatches) => {
changes.push(...patches)
inverseChanges.push(...inversePatches)
}
)
// In the meantime, our original state is replaced, as, for example,
// some changes were received from the server
state = produce(state, draft => {
draft.name = "Michel"
})
// When the wizard finishes (successfully) we can replay the changes that were in the fork onto the *new* state!
state = applyPatches(state, changes)
// state now contains the changes from both code paths!
expect(state).toEqual({
name: "Michel", // changed by the server
age: 33 // changed by the wizard
})
// Finally, even after finishing the wizard, the user might change his mind and undo his changes...
state = applyPatches(state, inverseChanges)
expect(state).toEqual({
name: "Michel", // Not reverted
age: 32 // Reverted
})
```
The generated patches are similar (but not the same) to the [RFC-6902 JSON patch standard](http://tools.ietf.org/html/rfc6902), except that the `path` property is an array, rather than a string. This makes processing patches easier. If you want to normalize to the official specification, `patch.path = patch.path.join("/")` should do the trick. Anyway, this is what a bunch of patches and their inverse could look like:
```json
[
{
"op": "replace",
"path": ["profile"],
"value": {"name": "Veria", "age": 5}
},
{"op": "remove", "path": ["tags", 3]}
]
```
```json
[
{"op": "replace", "path": ["profile"], "value": {"name": "Noa", "age": 6}},
{"op": "add", "path": ["tags", 3], "value": "kiddo"}
]
```
### `produceWithPatches`
Instead of setting up a patch listener, an easier way to obtain the patches is to use `produceWithPatches`, which has the same signature as `produce`, except that it doesn't return just the next state, but a tuple consisting of `[nextState, patches, inversePatches]`. Like `produce`, `produceWithPatches` supports currying as well.
```javascript
import {produceWithPatches} from "immer"
const [nextState, patches, inversePatches] = produceWithPatches(
{
age: 33
},
draft => {
draft.age++
}
)
```
Which produces:
```javascript
;[
{
age: 34
},
[
{
op: "replace",
path: ["age"],
value: 34
}
],
[
{
op: "replace",
path: ["age"],
value: 33
}
]
]
```
For a more in-depth study, see [Distributing patches and rebasing actions using Immer](https://medium.com/@mweststrate/distributing-state-changes-using-snapshots-patches-and-actions-part-2-2f50d8363988)
Tip: Check this trick to [compress patches](https://medium.com/@david.b.edelstein/using-immer-to-compress-immer-patches-f382835b6c69) produced over time.
## Async producers
It is allowed to return Promise objects from recipes. Or, in other words, to use `async / await`. This can be pretty useful for long running processes, that only produce the new object once the promise chain resolves. Note that `produce` itself (even in the curried form) will return a promise if the producer is async. Example:
```javascript
import produce from "immer"
const user = {
name: "michel",
todos: []
}
const loadedUser = await produce(user, async function(draft) {
draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
})
```
_Warning: please note that the draft shouldn't be 'leaked' from the async process and stored else where. The draft will still be revoked as soon as the async process completes._
## `createDraft` and `finishDraft`
`createDraft` and `finishDraft` are two low-level functions that are mostly useful for libraries that build abstractions on top of immer. It avoids the need to always create a function in order to work with drafts. Instead, one can create a draft, modify it, and at some time in the future finish the draft, in which case the next immutable state will be produced. We could for example rewrite our above example as:
```javascript
import {createDraft, finishDraft} from "immer"
const user = {
name: "michel",
todos: []
}
const draft = createDraft(user)
draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
const loadedUser = finishDraft(draft)
```
Note: `finishDraft` takes a `patchListener` as second argument, which can be used to record the patches, similarly to `produce`.
_Warning: in general, we recommend to use `produce` instead of the `createDraft` / `finishDraft` combo, `produce` is less error prone in usage, and more clearly separates the concepts of mutability and immutability in your code base._
## 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 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.users.push(action.payload)
return
}
})
```
_Note: It is not possible to return `undefined` this way, as it is indistinguishable from *not* updating the draft! Read on..._
## Producing `undefined` using `nothing`
So, in general, one can replace the current state by just `return`ing a new value from the producer, rather than modifying the draft. There is a subtle edge case however: if you try to write a producer that wants to replace the current state with `undefined`:
```javascript
produce({}, draft => {
// don't do anything
})
```
Versus:
```javascript
produce({}, draft => {
// Try to return undefined from the producer
return undefined
})
```
The problem is that in JavaScript a function that doesn't return anything also returns `undefined`! So immer cannot differentiate between those different cases. So, by default, Immer will assume that any producer that returns `undefined` just tried to modify the draft.
However, to make it clear to Immer that you intentionally want to produce the value `undefined`, you can return the built-in token `nothing`:
```javascript
import produce, {nothing} from "immer"
const state = {
hello: "world"
}
produce(state, draft => {})
produce(state, draft => undefined)
// Both return the original state: { hello: "world"}
produce(state, draft => nothing)
// Produces a new state, 'undefined'
```
N.B. Note that this problem is specific for the `undefined` value, any other value, including `null`, doesn't suffer from this issue.
## Inline shortcuts using `void`
Draft mutations in Immer usually warrant a code block, since a return denotes an overwrite. Sometimes that can stretch code a little more than you might be comfortable with.
In such cases, you can use javascripts [`void`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void) operator, which evaluates expressions and returns `undefined`.
```javascript
// Single mutation
produce(draft => void (draft.user.age += 1))
// Multiple mutations
produce(draft => void ((draft.user.age += 1), (draft.user.height = 186)))
```
Code style is highly personal, but for code bases that are to be understood by many, we recommend to stick to the classic `draft => { draft.user.age += 1}` to avoid cognitive overhead.
## Extracting the original object from a proxied instance
Immer exposes a named export `original` that will get the original object from the proxied instance inside `produce` (or return `undefined` for unproxied values). A good example of when this can be useful is when searching for nodes in a tree-like state using strict equality.
```js
import {original} from "immer"
const baseState = {users: [{name: "Richie"}]}
const nextState = produce(baseState, draftState => {
original(draftState.users) // is === baseState.users
})
```
Just want to know if a value is a proxied instance? Use the `isDraft` function!
```js
import {isDraft} from "immer"
const baseState = {users: [{name: "Bobby"}]}
const nextState = produce(baseState, draft => {
isDraft(draft) // => true
isDraft(draft.users) // => true
isDraft(draft.users[0]) // => true
})
isDraft(nextState) // => false
```
## Auto freezing
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.
_⚠️ If auto freezing is enabled, recipes are not entirely side-effect free: Any plain object or array that ends up in the produced result, will be frozen, even when these objects were not frozen before the start of the producer! ⚠️_
## Immer on older JavaScript environments?
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 (< v0.59) on Android. In such cases, Immer will fallback to an ES5 compatible implementation which works identical, but is a bit slower.
## Importing immer
`produce` is exposed as the default export, but optionally it can be used as name import as well, as this benefits some older project setups. So the following imports are all correct, where the first is recommended:
```javascript
import produce from "immer"
import {produce} from "immer"
const {produce} = require("immer")
const produce = require("immer").produce
const produce = require("immer").default
import unleashTheMagic from "immer"
import {produce as unleashTheMagic} from "immer"
```
## Supported object types
Plain objects and arrays are always drafted by Immer.
Every other object must use the `immerable` symbol to mark itself as compatible with Immer. When one of these objects is mutated within a producer, its prototype is preserved between copies.
```js
import {immerable} from "immer"
class Foo {
[immerable] = true // Option 1
constructor() {
this[immerable] = true // Option 2
}
}
Foo[immerable] = true // Option 3
```
For arrays, only numeric properties and the `length` property can be mutated. Custom properties are not preserved on arrays.
When working with `Date` objects, you should always create a new `Date` instance instead of mutating an existing `Date` object.
Built-in classes like `Map` and `Set` are not supported. As a workaround, you should clone them before mutating them in a producer:
```js
const state = {
set: new Set(),
map: new Map()
}
const nextState = produce(state, draft => {
// Don't use any Set methods, as that mutates the instance!
draft.set.add("foo") // ❌
// 1. Instead, clone the set (just once)
const newSet = new Set(draft.set) // ✅
// 2. Mutate the clone (just in this producer)
newSet.add("foo")
// 3. Update the draft with the new set
draft.set = newSet
// Similarly, don't use any Map methods.
draft.map.set("foo", "bar") // ❌
// 1. Instead, clone the map (just once)
const newMap = new Map(draft.map) // ✅
// 2. Mutate it
newMap.set("foo", "bar")
// 3. Update the draft
draft.map = newMap
})
```
## TypeScript or Flow
The Immer package ships with type definitions inside the package, which should be picked up by TypeScript and Flow out of the box and without further configuration.
The TypeScript typings automatically remove `readonly` modifiers from your draft types and return a value that matches your original type. See this practical example:
```ts
import produce from "immer"
interface State {
readonly x: number
}
// `x` cannot be modified here
const state: State = {
x: 0
}
const newState = produce(state, draft => {
// `x` can be modified here
draft.x++
})
// `newState.x` cannot be modified here
```
This ensures that the only place you can modify your state is in your produce callbacks. It even works recursively and with `ReadonlyArray`s!
For curried reducers, the type is inferred from the first argument of recipe function, so make sure to type it. The `Draft` utility type can be used if the state argument type is immutable:
```ts
import produce, {Draft} from "immer"
interface State {
readonly x: number
}
// `x` cannot be modified here
const state: State = {
x: 0
}
const increment = produce((draft: Draft<State>, inc: number) => {
// `x` can be modified here
draft.x += inc
})
const newState = increment(state, 2)
// `newState.x` cannot be modified here
```
**Note:** Immer v1.9+ supports TypeScript v3.1+ only.
**Note:** Immer v3.0+ supports TypeScript v3.4+ only.
# Pitfalls
1. Don't redefine draft like, `draft = myCoolNewState`. Instead, either modify the `draft` or return a new state. See [Returning data from producers](#returning-data-from-producers).
1. Immer assumes your state to be a unidirectional tree. That is, no object should appear twice in the tree, and there should be no circular references.
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`. Also, realize that immer is opt-in everywhere, so it is perfectly fine to manually write super performance critical reducers, and use immer for all the normal ones. Also note that `original` can be used to get the original state of an object, which is cheaper to read.
1. Always try to pull `produce` 'up', for example `for (let x of y) produce(base, d => d.push(x))` is exponentially slower than `produce(base, d => { for (let x of y) d.push(x)})`
1. It is possible to return values from producers, except, it is not possible to return `undefined` that way, as it is indistinguishable from not updating the draft at all! If you want to replace the draft with `undefined`, just return `nothing` from the producer.
## Cool things built with immer
- [react-copy-write](https://github.com/aweary/react-copy-write) _Immutable state with a mutable API_
- [redux-starter-kit](https://github.com/markerikson/redux-starter-kit) _A simple set of tools to make using Redux easier_
- [immer based handleActions](https://gist.github.com/kitze/fb65f527803a93fb2803ce79a792fff8) _Boilerplate free actions for Redux_
- [redux-box](https://github.com/anish000kumar/redux-box) _Modular and easy-to-grasp redux based state management, with least boilerplate_
- [quick-redux](https://github.com/jeffreyyoung/quick-redux) _tools to make redux development quicker and easier_
- [bey](https://github.com/jamiebuilds/bey) _Simple immutable state for React using Immer_
- [immer-wieder](https://github.com/drcmda/immer-wieder#readme) _State management lib that combines React 16 Context and immer for Redux semantics_
- [robodux](https://github.com/neurosnap/robodux) _flexible way to reduce redux boilerplate_
- [immer-reducer](https://github.com/epeli/immer-reducer) _Type-safe and terse React (useReducer()) and Redux reducers with Typescript_
- [redux-ts-utils](https://github.com/knpwrs/redux-ts-utils) _Everything you need to create type-safe applications with Redux with a strong emphasis on simplicity_
- [react-state-tree](https://github.com/suchipi/react-state-tree) _Drop-in replacement for useState that persists your state into a redux-like state tree_
- [redux-immer](https://github.com/salvoravida/redux-immer) _is used to create an equivalent function of Redux combineReducers that works with `immer` state. Like `redux-immutable` but for `immer`_
- ... and [many more](https://www.npmjs.com/browse/depended/immer)
## How does Immer work?
Read the (second part of the) [introduction blog](https://medium.com/@mweststrate/introducing-immer-immutability-the-easy-way-9d73d8f71cb3).
## Example patterns.
_For those who have to go back to thinking in object updates :-)_
```javascript
import produce from "immer"
// object mutations
const todosObj = {
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"}
})
// delete
const deletedTodosObj = produce(todosObj, draft => {
delete draft["id1"]
})
// update
const updatedTodosObj = produce(todosObj, draft => {
draft["id1"].done = true
})
// array mutations
const todosArray = [
{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"})
})
// delete
const deletedTodosArray = produce(todosArray, draft => {
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
})
```
## Performance
Here is a [simple benchmark](__performance_tests__/todo.js) on the performance of Immer. This test takes 50,000 todo items and updates 5,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 9.3.0. Use `yarn test:perf` to reproduce them locally.
![performance.png](images/performance.png)
Most important observation:
- Immer with proxies is roughly speaking twice to three times slower as a handwritten reducer (the above test case is worst case, see `yarn test:perf` for more tests). This is in practice negligible.
- Immer is roughly as fast as ImmutableJS. However, the _immutableJS + toJS_ makes clear the cost that often needs to be paid later; converting the immutableJS objects back to plain objects, to be able to pass them to components, over the network etc... (And there is also the upfront cost of converting data received from e.g. the server to immutable JS)
- Generating patches doesn't significantly slow immer down
- The ES5 fallback implementation is roughly twice as slow as the proxy implementation, in some cases worse.
## Migration
**Immer 2.\* -> 3.0**
In your producers, make sure you're not treating `this` as the draft. (see here: https://github.com/immerjs/immer/issues/308)
Upgrade to `typescript@^3.4` if you're a TypeScript user.
**Immer 1.\* -> 2.0**
Make sure you don't return any promises as state, because `produce` will actually invoke the promise and wait until it settles.
**Immer 2.1 -> 2.2**
When using TypeScript, for curried reducers that are typed in the form `produce<Type>((arg) => { })`, rewrite this to `produce((arg: Type) => { })` or `produce((arg: Draft<Type>) => { })` for correct inference.
## FAQ
_(for those who skimmed the above instead of actually reading)_
**Q: Does Immer use structural sharing? So that my selectors can be memoized and such?**
A: Yes
**Q: Does Immer support deep updates?**
A: Yes
**Q: I can't rely on Proxies being present on my target environments. Can I use Immer?**
A: Yes
**Q: Can I typecheck my data structures when using Immer?**
A: Yes
**Q: Can I store `Date` objects, functions etc in my state tree when using Immer?**
A: Yes
**Q: Is it fast?**
A: Yes
**Q: Idea! Can Immer freeze the state for me?**
A: Yes
## Credits
Special thanks to @Mendix, which supports its employees to experiment completely freely two full days a month, which formed the kick-start for this project.
## Donations
A significant part of my OSS work is unpaid. So [donations](https://mobx.js.org/donate.html) are greatly appreciated :)
https://github.com/immerjs/immer/releases

@@ -22,3 +22,6 @@ import * as legacyProxy from "./es5"

const configDefaults = {
useProxies: typeof Proxy !== "undefined" && typeof Reflect !== "undefined",
useProxies:
typeof Proxy !== "undefined" &&
typeof Proxy.revocable !== "undefined" &&
typeof Reflect !== "undefined",
autoFreeze:

@@ -25,0 +28,0 @@ typeof process !== "undefined"

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc