react-updater
Advanced tools
Comparing version 1.1.1 to 1.2.0
@@ -9,54 +9,4 @@ (function (global, factory) { | ||
var stringifyFunction = function stringifyFunction(fn) { | ||
return '[Function ' + fn.name + ']'; | ||
}; | ||
var noop = function noop() {}; | ||
var stringify = function stringify(value) { | ||
return JSON.stringify(value, function (key, value) { | ||
if (typeof value === 'function') { | ||
return stringifyFunction(value); | ||
} | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
return value; | ||
}); | ||
}; | ||
/*! | ||
* isobject <https://github.com/jonschlinkert/isobject> | ||
* | ||
* Copyright (c) 2014-2015, Jon Schlinkert. | ||
* Licensed under the MIT License. | ||
*/ | ||
var index$2 = function isObject(val) { | ||
return val != null && typeof val === 'object' && Array.isArray(val) === false; | ||
}; | ||
function isObjectObject(o) { | ||
return index$2(o) === true | ||
&& Object.prototype.toString.call(o) === '[object Object]'; | ||
} | ||
var index$1 = function isPlainObject(o) { | ||
var ctor,prot; | ||
if (isObjectObject(o) === false) return false; | ||
// If has modified constructor | ||
ctor = o.constructor; | ||
if (typeof ctor !== 'function') return false; | ||
// If has modified prototype | ||
prot = ctor.prototype; | ||
if (isObjectObject(prot) === false) return false; | ||
// If constructor does not have an Object-specific method | ||
if (prot.hasOwnProperty('isPrototypeOf') === false) { | ||
return false; | ||
} | ||
// Most likely a plain Object | ||
return true; | ||
}; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
@@ -102,6 +52,10 @@ | ||
_this.state = index$1(state) ? state : _defineProperty({}, STATE_PROPERTY_NAME, state); | ||
_this.state = Object.prototype.toString.call(state) === '[object Object]' ? state : _defineProperty({}, STATE_PROPERTY_NAME, state); | ||
return _this; | ||
} | ||
/** | ||
* Clean up. | ||
*/ | ||
_createClass(WithUpdater, [{ | ||
@@ -114,2 +68,6 @@ key: 'componentWillUnmount', | ||
/** | ||
* Create callback handler. | ||
*/ | ||
/** | ||
* Wraps the callback handler and returns a new function that receives | ||
@@ -129,2 +87,8 @@ * additional arguments. | ||
key: 'render', | ||
/** | ||
* Render. | ||
*/ | ||
value: function render() { | ||
@@ -147,3 +111,3 @@ var state = !(STATE_PROPERTY_NAME in this.state) ? this.state : this.state[STATE_PROPERTY_NAME]; | ||
this.memoizedCallbackHandlers = {}; | ||
this.memoizedCallbackHandlers = new WeakMap(); | ||
@@ -156,36 +120,36 @@ this.createCallbackHandler = function (name, createHandler) { | ||
if ("development" !== 'production' && !callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Callbacks handlers defined with anonymous functions should be' + ' avoided. This can lead to de-optimisations on components' + ' that rely on props equality.'); | ||
{ | ||
if (typeof callback !== 'function') { | ||
// eslint-disable-next-line no-console | ||
console.error('The given callback of type ' + (typeof callback === 'undefined' ? 'undefined' : _typeof(callback)) + ' should be a function.'); | ||
return noop; | ||
return function () {}; | ||
} | ||
if (!callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Callbacks handlers defined with anonymous functions should' + ' be avoided. This can lead to de-optimizations on' + ' components that rely on props equality. If you are seeing' + ' this message on older browsers and you are not passing an' + ' anonymous function you need to use a polyfill for' + ' Function.name.'); | ||
} | ||
} | ||
var id = name + stringifyFunction(callback) + stringify(params); | ||
if (!_this2.memoizedCallbackHandlers.has(callback)) { | ||
var _handler = createHandler(callback); | ||
if (!_this2.memoizedCallbackHandlers[id]) { | ||
var handler = createHandler(callback, params); | ||
_this2.memoizedCallbackHandlers.set(callback, _handler); | ||
_this2.memoizedCallbackHandlers[id] = { callback: callback, handler: handler }; | ||
_handler.data = params; | ||
return handler; | ||
return _handler; | ||
} | ||
// We need to ensure the handler is updated for different callbacks. | ||
// Since we check for the callback.name property, if another callback | ||
// with the same `name` were passed, the returned handler would the | ||
// call the previous callback. | ||
if (_this2.memoizedCallbackHandlers[id].callback !== callback) { | ||
_this2.memoizedCallbackHandlers[id] = { | ||
callback: callback, | ||
handler: createHandler(callback, params) | ||
}; | ||
} | ||
var handler = _this2.memoizedCallbackHandlers.get(callback); | ||
return _this2.memoizedCallbackHandlers[id].handler; | ||
handler.data = params; | ||
return handler; | ||
}; | ||
}; | ||
this.handle = this.createCallbackHandler('handle', function (callback, params) { | ||
return function () { | ||
this.handle = this.createCallbackHandler('handle', function (callback) { | ||
var handler = function handler() { | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
@@ -195,7 +159,9 @@ args[_key2] = arguments[_key2]; | ||
return callback.apply(undefined, _toConsumableArray(params).concat(args)); | ||
return callback.apply(undefined, _toConsumableArray(handler.data).concat(args)); | ||
}; | ||
return handler; | ||
}); | ||
this.update = this.createCallbackHandler('update', function (callback, params) { | ||
return function () { | ||
this.update = this.createCallbackHandler('update', function (callback) { | ||
var handler = function handler() { | ||
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
@@ -207,2 +173,7 @@ args[_key3] = arguments[_key3]; | ||
// A synthetic event cannot be accessed in an asynchronous way - | ||
// e.g. inside `setState()` - so we need to call `event.persist()` | ||
// event to remove the synthetic event from the pool. | ||
// We clean up the event manually when the callback of `setState()` is | ||
// invoked. | ||
var _iteratorNormalCompletion = true; | ||
@@ -240,6 +211,6 @@ var _didIteratorError = false; | ||
if (!(STATE_PROPERTY_NAME in state)) { | ||
return callback.apply(undefined, [state].concat(_toConsumableArray(params), args)); | ||
return callback.apply(undefined, [state].concat(_toConsumableArray(handler.data), args)); | ||
} | ||
return _defineProperty({}, STATE_PROPERTY_NAME, callback.apply(undefined, [state[STATE_PROPERTY_NAME]].concat(_toConsumableArray(params), args))); | ||
return _defineProperty({}, STATE_PROPERTY_NAME, callback.apply(undefined, [state[STATE_PROPERTY_NAME]].concat(_toConsumableArray(handler.data), args))); | ||
}, function () { | ||
@@ -251,2 +222,4 @@ if (event && typeof event.destructor === 'function') { | ||
}; | ||
return handler; | ||
}); | ||
@@ -253,0 +226,0 @@ }; |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):t.withUpdater=e(t.React)}(this,function(t){"use strict";function e(t){return!0===f(t)&&"[object Object]"===Object.prototype.toString.call(t)}function n(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);t.length>e;e++)n[e]=t[e];return n}return Array.from(t)}function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var c="default"in t?t.default:t,u=function(t){return"[Function "+t.name+"]"},l=function(t){return JSON.stringify(t,function(t,e){return"function"==typeof e?u(e):e})},f=function(t){return null!=t&&"object"==typeof t&&!1===Array.isArray(t)},s=function(t){var n,r;return!1!==e(t)&&("function"==typeof(n=t.constructor)&&(r=n.prototype,!1!==e(r)&&!1!==r.hasOwnProperty("isPrototypeOf")))},p=function(){function t(t,e){for(var n=0;e.length>n;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return function(e,n,r){return n&&t(e.prototype,n),r&&t(e,r),e}}(),d="@@STATE";return function(e){return function(f){var y=function(t){function n(t){o(this,n);var i=a(this,(n.__proto__||Object.getPrototypeOf(n)).call(this,t));h.call(i);var c="function"==typeof e?e(t):e;return i.state=s(c)?c:r({},d,c),i}return i(n,t),p(n,[{key:"componentWillUnmount",value:function(){this.memoizedCallbackHandlers=null}},{key:"render",value:function(){var t=d in this.state?this.state[d]:this.state;return c.createElement(f,Object.assign({},this.props,{handle:this.handle,state:t,update:this.update}))}}]),n}(t.Component),h=function(){var t=this;this.memoizedCallbackHandlers={},this.createCallbackHandler=function(e,n){return function(r){for(var o=arguments.length,a=Array(o>1?o-1:0),i=1;o>i;i++)a[i-1]=arguments[i];var c=e+u(r)+l(a);if(!t.memoizedCallbackHandlers[c]){var f=n(r,a);return t.memoizedCallbackHandlers[c]={callback:r,handler:f},f}return t.memoizedCallbackHandlers[c].callback!==r&&(t.memoizedCallbackHandlers[c]={callback:r,handler:n(r,a)}),t.memoizedCallbackHandlers[c].handler}},this.handle=this.createCallbackHandler("handle",function(t,e){return function(){for(var r=arguments.length,o=Array(r),a=0;r>a;a++)o[a]=arguments[a];return t.apply(void 0,n(e).concat(o))}}),this.update=this.createCallbackHandler("update",function(e,o){return function(){for(var a=arguments.length,i=Array(a),c=0;a>c;c++)i[c]=arguments[c];var u=void 0,l=!0,f=!1,s=void 0;try{for(var p,y=i[Symbol.iterator]();!(l=(p=y.next()).done);l=!0){var h=p.value;if(h&&"function"==typeof h.persist){(u=h).persist();break}}}catch(t){f=!0,s=t}finally{try{!l&&y.return&&y.return()}finally{if(f)throw s}}t.setState(function(t){return d in t?r({},d,e.apply(void 0,[t[d]].concat(n(o),i))):e.apply(void 0,[t].concat(n(o),i))},function(){u&&"function"==typeof u.destructor&&u.destructor()})}})};return y}}}); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):t.withUpdater=e(t.React)}(this,function(t){"use strict";function e(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);t.length>e;e++)n[e]=t[e];return n}return Array.from(t)}function n(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function o(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var i="default"in t?t.default:t,u=function(){function t(t,e){for(var n=0;e.length>n;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return function(e,n,r){return n&&t(e.prototype,n),r&&t(e,r),e}}(),c="@@STATE";return function(l){return function(f){var s=function(t){function e(t){r(this,e);var o=a(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t));d.call(o);var i="function"==typeof l?l(t):l;return o.state="[object Object]"===Object.prototype.toString.call(i)?i:n({},c,i),o}return o(e,t),u(e,[{key:"componentWillUnmount",value:function(){this.memoizedCallbackHandlers=null}},{key:"render",value:function(){var t=c in this.state?this.state[c]:this.state;return i.createElement(f,Object.assign({},this.props,{handle:this.handle,state:t,update:this.update}))}}]),e}(t.Component),d=function(){var t=this;this.memoizedCallbackHandlers=new WeakMap,this.createCallbackHandler=function(e,n){return function(e){for(var r=arguments.length,a=Array(r>1?r-1:0),o=1;r>o;o++)a[o-1]=arguments[o];if(!t.memoizedCallbackHandlers.has(e)){var i=n(e);return t.memoizedCallbackHandlers.set(e,i),i.data=a,i}var u=t.memoizedCallbackHandlers.get(e);return u.data=a,u}},this.handle=this.createCallbackHandler("handle",function(t){return function n(){for(var r=arguments.length,a=Array(r),o=0;r>o;o++)a[o]=arguments[o];return t.apply(void 0,e(n.data).concat(a))}}),this.update=this.createCallbackHandler("update",function(r){return function a(){for(var o=arguments.length,i=Array(o),u=0;o>u;u++)i[u]=arguments[u];var l=void 0,f=!0,s=!1,d=void 0;try{for(var p,h=i[Symbol.iterator]();!(f=(p=h.next()).done);f=!0){var y=p.value;if(y&&"function"==typeof y.persist){(l=y).persist();break}}}catch(t){s=!0,d=t}finally{try{!f&&h.return&&h.return()}finally{if(s)throw d}}t.setState(function(t){return c in t?n({},c,r.apply(void 0,[t[c]].concat(e(a.data),i))):r.apply(void 0,[t].concat(e(a.data),i))},function(){l&&"function"==typeof l.destructor&&l.destructor()})}})};return s}}}); |
@@ -0,1 +1,3 @@ | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
@@ -18,4 +20,2 @@ | ||
import React, { Component } from 'react'; | ||
import { noop, stringify, stringifyFunction } from './utils'; | ||
import isPlainObject from 'is-plain-object'; | ||
@@ -46,6 +46,10 @@ /** | ||
_this.state = isPlainObject(state) ? state : _defineProperty({}, STATE_PROPERTY_NAME, state); | ||
_this.state = Object.prototype.toString.call(state) === '[object Object]' ? state : _defineProperty({}, STATE_PROPERTY_NAME, state); | ||
return _this; | ||
} | ||
/** | ||
* Clean up. | ||
*/ | ||
_createClass(WithUpdater, [{ | ||
@@ -58,2 +62,6 @@ key: 'componentWillUnmount', | ||
/** | ||
* Create callback handler. | ||
*/ | ||
/** | ||
* Wraps the callback handler and returns a new function that receives | ||
@@ -73,2 +81,8 @@ * additional arguments. | ||
key: 'render', | ||
/** | ||
* Render. | ||
*/ | ||
value: function render() { | ||
@@ -91,3 +105,3 @@ var state = !(STATE_PROPERTY_NAME in this.state) ? this.state : this.state[STATE_PROPERTY_NAME]; | ||
this.memoizedCallbackHandlers = {}; | ||
this.memoizedCallbackHandlers = new WeakMap(); | ||
@@ -100,36 +114,36 @@ this.createCallbackHandler = function (name, createHandler) { | ||
if (process.env.NODE_ENV !== 'production' && !callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Callbacks handlers defined with anonymous functions should be' + ' avoided. This can lead to de-optimisations on components' + ' that rely on props equality.'); | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (typeof callback !== 'function') { | ||
// eslint-disable-next-line no-console | ||
console.error('The given callback of type ' + (typeof callback === 'undefined' ? 'undefined' : _typeof(callback)) + ' should be a function.'); | ||
return noop; | ||
return function () {}; | ||
} | ||
if (!callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Callbacks handlers defined with anonymous functions should' + ' be avoided. This can lead to de-optimizations on' + ' components that rely on props equality. If you are seeing' + ' this message on older browsers and you are not passing an' + ' anonymous function you need to use a polyfill for' + ' Function.name.'); | ||
} | ||
} | ||
var id = name + stringifyFunction(callback) + stringify(params); | ||
if (!_this2.memoizedCallbackHandlers.has(callback)) { | ||
var _handler = createHandler(callback); | ||
if (!_this2.memoizedCallbackHandlers[id]) { | ||
var handler = createHandler(callback, params); | ||
_this2.memoizedCallbackHandlers.set(callback, _handler); | ||
_this2.memoizedCallbackHandlers[id] = { callback: callback, handler: handler }; | ||
_handler.data = params; | ||
return handler; | ||
return _handler; | ||
} | ||
// We need to ensure the handler is updated for different callbacks. | ||
// Since we check for the callback.name property, if another callback | ||
// with the same `name` were passed, the returned handler would the | ||
// call the previous callback. | ||
if (_this2.memoizedCallbackHandlers[id].callback !== callback) { | ||
_this2.memoizedCallbackHandlers[id] = { | ||
callback: callback, | ||
handler: createHandler(callback, params) | ||
}; | ||
} | ||
var handler = _this2.memoizedCallbackHandlers.get(callback); | ||
return _this2.memoizedCallbackHandlers[id].handler; | ||
handler.data = params; | ||
return handler; | ||
}; | ||
}; | ||
this.handle = this.createCallbackHandler('handle', function (callback, params) { | ||
return function () { | ||
this.handle = this.createCallbackHandler('handle', function (callback) { | ||
var handler = function handler() { | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
@@ -139,7 +153,9 @@ args[_key2] = arguments[_key2]; | ||
return callback.apply(undefined, _toConsumableArray(params).concat(args)); | ||
return callback.apply(undefined, _toConsumableArray(handler.data).concat(args)); | ||
}; | ||
return handler; | ||
}); | ||
this.update = this.createCallbackHandler('update', function (callback, params) { | ||
return function () { | ||
this.update = this.createCallbackHandler('update', function (callback) { | ||
var handler = function handler() { | ||
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
@@ -151,2 +167,7 @@ args[_key3] = arguments[_key3]; | ||
// A synthetic event cannot be accessed in an asynchronous way - | ||
// e.g. inside `setState()` - so we need to call `event.persist()` | ||
// event to remove the synthetic event from the pool. | ||
// We clean up the event manually when the callback of `setState()` is | ||
// invoked. | ||
var _iteratorNormalCompletion = true; | ||
@@ -184,6 +205,6 @@ var _didIteratorError = false; | ||
if (!(STATE_PROPERTY_NAME in state)) { | ||
return callback.apply(undefined, [state].concat(_toConsumableArray(params), args)); | ||
return callback.apply(undefined, [state].concat(_toConsumableArray(handler.data), args)); | ||
} | ||
return _defineProperty({}, STATE_PROPERTY_NAME, callback.apply(undefined, [state[STATE_PROPERTY_NAME]].concat(_toConsumableArray(params), args))); | ||
return _defineProperty({}, STATE_PROPERTY_NAME, callback.apply(undefined, [state[STATE_PROPERTY_NAME]].concat(_toConsumableArray(handler.data), args))); | ||
}, function () { | ||
@@ -195,2 +216,4 @@ if (event && typeof event.destructor === 'function') { | ||
}; | ||
return handler; | ||
}); | ||
@@ -197,0 +220,0 @@ }; |
@@ -7,2 +7,4 @@ 'use strict'; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
@@ -14,8 +16,2 @@ | ||
var _utils = require('./utils'); | ||
var _isPlainObject = require('is-plain-object'); | ||
var _isPlainObject2 = _interopRequireDefault(_isPlainObject); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -59,6 +55,10 @@ | ||
_this.state = (0, _isPlainObject2.default)(state) ? state : _defineProperty({}, STATE_PROPERTY_NAME, state); | ||
_this.state = Object.prototype.toString.call(state) === '[object Object]' ? state : _defineProperty({}, STATE_PROPERTY_NAME, state); | ||
return _this; | ||
} | ||
/** | ||
* Clean up. | ||
*/ | ||
_createClass(WithUpdater, [{ | ||
@@ -71,2 +71,6 @@ key: 'componentWillUnmount', | ||
/** | ||
* Create callback handler. | ||
*/ | ||
/** | ||
* Wraps the callback handler and returns a new function that receives | ||
@@ -86,2 +90,8 @@ * additional arguments. | ||
key: 'render', | ||
/** | ||
* Render. | ||
*/ | ||
value: function render() { | ||
@@ -104,3 +114,3 @@ var state = !(STATE_PROPERTY_NAME in this.state) ? this.state : this.state[STATE_PROPERTY_NAME]; | ||
this.memoizedCallbackHandlers = {}; | ||
this.memoizedCallbackHandlers = new WeakMap(); | ||
@@ -113,36 +123,36 @@ this.createCallbackHandler = function (name, createHandler) { | ||
if (process.env.NODE_ENV !== 'production' && !callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Callbacks handlers defined with anonymous functions should be' + ' avoided. This can lead to de-optimisations on components' + ' that rely on props equality.'); | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (typeof callback !== 'function') { | ||
// eslint-disable-next-line no-console | ||
console.error('The given callback of type ' + (typeof callback === 'undefined' ? 'undefined' : _typeof(callback)) + ' should be a function.'); | ||
return _utils.noop; | ||
return function () {}; | ||
} | ||
if (!callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Callbacks handlers defined with anonymous functions should' + ' be avoided. This can lead to de-optimizations on' + ' components that rely on props equality. If you are seeing' + ' this message on older browsers and you are not passing an' + ' anonymous function you need to use a polyfill for' + ' Function.name.'); | ||
} | ||
} | ||
var id = name + (0, _utils.stringifyFunction)(callback) + (0, _utils.stringify)(params); | ||
if (!_this2.memoizedCallbackHandlers.has(callback)) { | ||
var _handler = createHandler(callback); | ||
if (!_this2.memoizedCallbackHandlers[id]) { | ||
var handler = createHandler(callback, params); | ||
_this2.memoizedCallbackHandlers.set(callback, _handler); | ||
_this2.memoizedCallbackHandlers[id] = { callback: callback, handler: handler }; | ||
_handler.data = params; | ||
return handler; | ||
return _handler; | ||
} | ||
// We need to ensure the handler is updated for different callbacks. | ||
// Since we check for the callback.name property, if another callback | ||
// with the same `name` were passed, the returned handler would the | ||
// call the previous callback. | ||
if (_this2.memoizedCallbackHandlers[id].callback !== callback) { | ||
_this2.memoizedCallbackHandlers[id] = { | ||
callback: callback, | ||
handler: createHandler(callback, params) | ||
}; | ||
} | ||
var handler = _this2.memoizedCallbackHandlers.get(callback); | ||
return _this2.memoizedCallbackHandlers[id].handler; | ||
handler.data = params; | ||
return handler; | ||
}; | ||
}; | ||
this.handle = this.createCallbackHandler('handle', function (callback, params) { | ||
return function () { | ||
this.handle = this.createCallbackHandler('handle', function (callback) { | ||
var handler = function handler() { | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
@@ -152,7 +162,9 @@ args[_key2] = arguments[_key2]; | ||
return callback.apply(undefined, _toConsumableArray(params).concat(args)); | ||
return callback.apply(undefined, _toConsumableArray(handler.data).concat(args)); | ||
}; | ||
return handler; | ||
}); | ||
this.update = this.createCallbackHandler('update', function (callback, params) { | ||
return function () { | ||
this.update = this.createCallbackHandler('update', function (callback) { | ||
var handler = function handler() { | ||
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
@@ -164,2 +176,7 @@ args[_key3] = arguments[_key3]; | ||
// A synthetic event cannot be accessed in an asynchronous way - | ||
// e.g. inside `setState()` - so we need to call `event.persist()` | ||
// event to remove the synthetic event from the pool. | ||
// We clean up the event manually when the callback of `setState()` is | ||
// invoked. | ||
var _iteratorNormalCompletion = true; | ||
@@ -197,6 +214,6 @@ var _didIteratorError = false; | ||
if (!(STATE_PROPERTY_NAME in state)) { | ||
return callback.apply(undefined, [state].concat(_toConsumableArray(params), args)); | ||
return callback.apply(undefined, [state].concat(_toConsumableArray(handler.data), args)); | ||
} | ||
return _defineProperty({}, STATE_PROPERTY_NAME, callback.apply(undefined, [state[STATE_PROPERTY_NAME]].concat(_toConsumableArray(params), args))); | ||
return _defineProperty({}, STATE_PROPERTY_NAME, callback.apply(undefined, [state[STATE_PROPERTY_NAME]].concat(_toConsumableArray(handler.data), args))); | ||
}, function () { | ||
@@ -208,2 +225,4 @@ if (event && typeof event.destructor === 'function') { | ||
}; | ||
return handler; | ||
}); | ||
@@ -210,0 +229,0 @@ }; |
{ | ||
"name": "react-updater", | ||
"description": "Functional stateful components made easy", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"browser": "dist/react-updater.js", | ||
@@ -6,0 +6,0 @@ "main": "lib/index.js", |
@@ -88,14 +88,43 @@ # React Updater | ||
**Important:** `update` memoizes the given state updaters in order to avoid a common pitfall associated with components that rely on props equality by using `shouldComponentUpdate`. Using classes this is avoided by using `this.onClick`, but if you pass inline functions as a state updater, `update()` will return a new callback handler. This can lead to de-optimizations because `shouldComponentUpdate` will return `true` on every render since `props.onClick !== nexProps.onClick`. This way we can ensure it will always returns the same reference for each handler. | ||
**Important:** `update` memoizes the given state updaters in order to avoid a common pitfall associated with components that rely on props equality by using `shouldComponentUpdate`. This can be avoided with the class syntax by using `this.onClick`, but if you pass inline functions as a state updater, `update()` will return a new callback handler. This can lead to de-optimizations because `shouldComponentUpdate` will return `true` on every render since `props.onClick !== nexProps.onClick`. | ||
If you you register the same callback with different data multiple times, ince the `update()` function memoizes the state updater and attaches the additional data to the new callback handler, so the expected parameters will be the ones passed on the last call of `update()`. To avoid this limitation consider the following example: | ||
```js | ||
// Bad. | ||
// This will log a warning message since the given handler is a anonymous function. | ||
const Component = props => <div onClick={props.update(state => state + 1)} />; | ||
// If you click on the `#first` button the final state will be "2" instead of "1", | ||
// since the final value corresponds to the last call of `update()` for the same | ||
// state updater, so the `step` parameter will be "2". | ||
const increment = (state, step) => state + step; | ||
// Good. | ||
const onClick = state => state + 1; | ||
const Component = props => <div onClick={props.update(onClick)} />; | ||
withUpdater(0)(props => ( | ||
<div> | ||
<h1>{props.state}</h1> | ||
<button id={'first'} onClick={props.update(increment, 1)} /> | ||
<button id={'second'} onClick={props.update(increment, 2)} /> | ||
</div> | ||
)); | ||
export default withUpdater(0)(Component); | ||
// Instead do the following: | ||
const increment = (state, step) => state + step; | ||
const incrementOne = state => increment(state, 1); | ||
const incrementTwo = state => increment(state, 2); | ||
withUpdater(0)(props => ( | ||
<div> | ||
<h1>{props.state}</h1> | ||
<button id={'first'} onClick={props.update(incrementOne)} /> | ||
<button id={'second'} onClick={props.update(incrementTwo)} /> | ||
</div> | ||
)); | ||
// However if the attached data has the same value it is ok to do the following: | ||
const increment = (state, props) => state + props.step; | ||
withUpdater(0)(props => ( | ||
<div> | ||
<h1>{props.state}</h1> | ||
<button id={'first'} onClick={props.update(increment, props)} /> | ||
<button id={'second'} onClick={props.update(increment, props)} /> | ||
</div> | ||
)); | ||
``` | ||
@@ -204,2 +233,5 @@ | ||
const increment = state => count(state, 'INCREMENT'); | ||
const decrement = state => count(state, 'DECREMENT'); | ||
const Counter = props => ( | ||
@@ -209,7 +241,7 @@ <div> | ||
<button onClick={props.update(count, 'DECREMENT')}> | ||
<button onClick={props.update(decrement)}> | ||
{'-'} | ||
</button> | ||
<button onClick={props.update(count, 'INCREMENT')}> | ||
<button onClick={props.update(increment)}> | ||
{'+'} | ||
@@ -216,0 +248,0 @@ </button> |
@@ -6,4 +6,2 @@ /** | ||
import React, { Component } from 'react'; | ||
import { noop, stringify, stringifyFunction } from './utils'; | ||
import isPlainObject from 'is-plain-object'; | ||
@@ -23,3 +21,3 @@ /** | ||
class WithUpdater extends Component { | ||
memoizedCallbackHandlers = {}; | ||
memoizedCallbackHandlers = new WeakMap(); | ||
@@ -33,3 +31,3 @@ constructor(props) { | ||
this.state = isPlainObject(state) | ||
this.state = Object.prototype.toString.call(state) === '[object Object]' | ||
? state | ||
@@ -39,2 +37,6 @@ : { [STATE_PROPERTY_NAME]: state }; | ||
/** | ||
* Clean up. | ||
*/ | ||
componentWillUnmount() { | ||
@@ -44,21 +46,38 @@ this.memoizedCallbackHandlers = null; | ||
/** | ||
* Create callback handler. | ||
*/ | ||
createCallbackHandler = (name, createHandler) => { | ||
return (callback, ...params) => { | ||
if (process.env.NODE_ENV !== 'production' && !callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn( | ||
'Callbacks handlers defined with anonymous functions should be' + | ||
' avoided. This can lead to de-optimisations on components' + | ||
' that rely on props equality.' | ||
); | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (typeof callback !== 'function') { | ||
// eslint-disable-next-line no-console | ||
console.error( | ||
`The given callback of type ${typeof callback}` + | ||
' should be a function.' | ||
); | ||
return noop; | ||
return () => {}; | ||
} | ||
if (!callback.name) { | ||
// eslint-disable-next-line no-console | ||
console.warn( | ||
'Callbacks handlers defined with anonymous functions should' + | ||
' be avoided. This can lead to de-optimizations on' + | ||
' components that rely on props equality. If you are seeing' + | ||
' this message on older browsers and you are not passing an' + | ||
' anonymous function you need to use a polyfill for' + | ||
' Function.name.' | ||
); | ||
} | ||
} | ||
const id = name + stringifyFunction(callback) + stringify(params); | ||
if (!this.memoizedCallbackHandlers.has(callback)) { | ||
const handler = createHandler(callback); | ||
if (!this.memoizedCallbackHandlers[id]) { | ||
const handler = createHandler(callback, params); | ||
this.memoizedCallbackHandlers.set(callback, handler); | ||
this.memoizedCallbackHandlers[id] = { callback, handler }; | ||
handler.data = params; | ||
@@ -68,14 +87,7 @@ return handler; | ||
// We need to ensure the handler is updated for different callbacks. | ||
// Since we check for the callback.name property, if another callback | ||
// with the same `name` were passed, the returned handler would the | ||
// call the previous callback. | ||
if (this.memoizedCallbackHandlers[id].callback !== callback) { | ||
this.memoizedCallbackHandlers[id] = { | ||
callback, | ||
handler: createHandler(callback, params) | ||
}; | ||
} | ||
const handler = this.memoizedCallbackHandlers.get(callback); | ||
return this.memoizedCallbackHandlers[id].handler; | ||
handler.data = params; | ||
return handler; | ||
}; | ||
@@ -89,4 +101,6 @@ }; | ||
handle = this.createCallbackHandler('handle', (callback, params) => { | ||
return (...args) => callback(...params, ...args); | ||
handle = this.createCallbackHandler('handle', callback => { | ||
const handler = (...args) => callback(...handler.data, ...args); | ||
return handler; | ||
}); | ||
@@ -102,6 +116,11 @@ | ||
update = this.createCallbackHandler('update', (callback, params) => { | ||
return (...args) => { | ||
update = this.createCallbackHandler('update', callback => { | ||
const handler = (...args) => { | ||
let event; | ||
// A synthetic event cannot be accessed in an asynchronous way - | ||
// e.g. inside `setState()` - so we need to call `event.persist()` | ||
// event to remove the synthetic event from the pool. | ||
// We clean up the event manually when the callback of `setState()` is | ||
// invoked. | ||
for (const arg of args) { | ||
@@ -119,3 +138,3 @@ if (arg && typeof arg.persist === 'function') { | ||
if (!(STATE_PROPERTY_NAME in state)) { | ||
return callback(state, ...params, ...args); | ||
return callback(state, ...handler.data, ...args); | ||
} | ||
@@ -126,3 +145,3 @@ | ||
state[STATE_PROPERTY_NAME], | ||
...params, | ||
...handler.data, | ||
...args | ||
@@ -139,4 +158,10 @@ ) | ||
}; | ||
return handler; | ||
}); | ||
/** | ||
* Render. | ||
*/ | ||
render() { | ||
@@ -143,0 +168,0 @@ const state = !(STATE_PROPERTY_NAME in this.state) |
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
46707
258
7
670