react-pure-events
Version:
A HOC wrapper that makes events props passed to React PureComponents pure
167 lines (127 loc) • 6.44 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
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; }; }();
exports.wrapPureEventProps = wrapPureEventProps;
exports.guardPureEventProps = guardPureEventProps;
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _hoistNonReactMethods = require('hoist-non-react-methods');
var _hoistNonReactMethods2 = _interopRequireDefault(_hoistNonReactMethods);
var _omit = require('lodash/omit');
var _omit2 = _interopRequireDefault(_omit);
var _flatten = require('lodash/flatten');
var _flatten2 = _interopRequireDefault(_flatten);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
// @todo add tests
// - Test that the various decorator formats work
// - test against the react version used by react-native
function makeDecorator(events, guard) {
events = (0, _flatten2.default)(events);
return function (BaseComponent) {
// @todo Hoist methods
var Wrapper = function (_Component) {
_inherits(Wrapper, _Component);
function Wrapper() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, Wrapper);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Wrapper.__proto__ || Object.getPrototypeOf(Wrapper)).call.apply(_ref, [this].concat(args))), _this), _this._setMainRef = function (ref) {
_this._mainRef = ref;
}, _this._eventHandlers = {}, _temp), _possibleConstructorReturn(_this, _ret);
}
// @todo Is it worth it to wrap propTypes now that they are deprecated?
_createClass(Wrapper, [{
key: '_eventHandlerFor',
value: function _eventHandlerFor(name) {
var _this2 = this;
if (!this._eventHandlers[name]) {
this._eventHandlers[name] = function () {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
if (typeof _this2.props[name] === 'function') {
return _this2.props[name].apply(null, args);
}
};
}
return this._eventHandlers[name];
}
}, {
key: 'render',
value: function render() {
var otherProps = (0, _omit2.default)(this.props, events);
var eventProps = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = events[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var event = _step.value;
var value = this.props[event];
if (guard || typeof value === 'function') {
eventProps[event] = this._eventHandlerFor(event);
} else if (event in this.props) {
eventProps[event] = value;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _react2.default.createElement(BaseComponent, _extends({
ref: this._setMainRef
}, otherProps, eventProps));
}
}]);
return Wrapper;
}(_react.Component);
Wrapper.displayName = 'wrapPureEventProps(' + (BaseComponent.displayName || BaseComponent.name || 'Component') + ')';
return (0, _hoistNonReactMethods2.default)(Wrapper, BaseComponent, function (component) {
return component._mainRef;
});
};
}
/**
* Wraps a PureComponent with a HOC that replaces a set of event props
* with functions that call the event prop, but if passed are the same on every
* render() call even if the actual function passed to the HOC changes.
*
* This guarantees that even if a caller uses `onEvent={() => {}}` which causes
* a new function instance to be passed every render the PureComponent will always
* receive the same prop and remain purely optimized.
*/
function wrapPureEventProps() {
for (var _len3 = arguments.length, events = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
events[_key3] = arguments[_key3];
}
return makeDecorator(events, false);
}
/**
* Does the same as @wrapPureEventProps, but also guarantees that if an event prop
* is not a function (not passed or invalid) a noop function will be passed instead.
*/
function guardPureEventProps() {
for (var _len4 = arguments.length, events = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
events[_key4] = arguments[_key4];
}
return makeDecorator(events, true);
}