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

uncontrollable

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

uncontrollable - npm Package Compare versions

Comparing version 4.1.0 to 5.0.0

167

index.js

@@ -1,31 +0,162 @@

'use strict';
"use strict";
exports.__esModule = true;
exports.default = uncontrollable;
var _createUncontrollable = require('./createUncontrollable');
var _react = _interopRequireDefault(require("react"));
var _createUncontrollable2 = _interopRequireDefault(_createUncontrollable);
var _invariant = _interopRequireDefault(require("invariant"));
var Utils = _interopRequireWildcard(require("./utils"));
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var mixin = {
shouldComponentUpdate: function shouldComponentUpdate() {
//let the forceUpdate trigger the update
return !this._notifying;
}
};
function _extends() { _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; }; return _extends.apply(this, arguments); }
function set(component, propName, handler, value, args) {
if (handler) {
component._notifying = true;
handler.call.apply(handler, [component, value].concat(args));
component._notifying = false;
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
function uncontrollable(Component, controlledValues, methods) {
if (methods === void 0) {
methods = [];
}
component._values[propName] = value;
var displayName = Component.displayName || Component.name || 'Component';
var isCompositeComponent = Utils.isReactComponent(Component);
var controlledProps = Object.keys(controlledValues);
var PROPS_TO_OMIT = controlledProps.map(Utils.defaultKey);
!(isCompositeComponent || !methods.length) ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, '[uncontrollable] stateless function components cannot pass through methods ' + 'because they have no associated instances. Check component: ' + displayName + ', ' + 'attempting to pass through methods: ' + methods.join(', ')) : invariant(false) : void 0;
if (!component.unmounted) component.forceUpdate();
var UncontrolledComponent =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(UncontrolledComponent, _React$Component);
function UncontrolledComponent() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
_this.handlers = Object.create(null);
controlledProps.forEach(function (propName) {
var handlerName = controlledValues[propName];
var handleChange = function handleChange(value) {
if (_this.props[handlerName]) {
var _this$props;
_this._notifying = true;
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
(_this$props = _this.props)[handlerName].apply(_this$props, [value].concat(args));
_this._notifying = false;
}
_this._values[propName] = value;
if (!_this.unmounted) _this.forceUpdate();
};
_this.handlers[handlerName] = handleChange;
});
if (isCompositeComponent) _this.attachRef = function (ref) {
_this.inner = ref;
};
return _this;
}
var _proto = UncontrolledComponent.prototype;
_proto.shouldComponentUpdate = function shouldComponentUpdate() {
//let the forceUpdate trigger the update
return !this._notifying;
};
_proto.componentWillMount = function componentWillMount() {
var _this2 = this;
var props = this.props;
this._values = Object.create(null);
controlledProps.forEach(function (key) {
_this2._values[key] = props[Utils.defaultKey(key)];
});
};
_proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
var _this3 = this;
var props = this.props;
controlledProps.forEach(function (key) {
/**
* If a prop switches from controlled to Uncontrolled
* reset its value to the defaultValue
*/
if (!Utils.isProp(nextProps, key) && Utils.isProp(props, key)) {
_this3._values[key] = nextProps[Utils.defaultKey(key)];
}
});
};
_proto.componentWillUnmount = function componentWillUnmount() {
this.unmounted = true;
};
_proto.getControlledInstance = function getControlledInstance() {
return this.inner;
};
_proto.render = function render() {
var _this4 = this;
var props = _extends({}, this.props);
PROPS_TO_OMIT.forEach(function (prop) {
delete props[prop];
});
var newProps = {};
controlledProps.forEach(function (propName) {
var propValue = _this4.props[propName];
newProps[propName] = propValue !== undefined ? propValue : _this4._values[propName];
});
return _react.default.createElement(Component, _extends({}, props, newProps, this.handlers, {
ref: this.attachRef
}));
};
return UncontrolledComponent;
}(_react.default.Component);
UncontrolledComponent.displayName = "Uncontrolled(" + displayName + ")";
UncontrolledComponent.propTypes = Utils.uncontrolledPropTypes(controlledValues, displayName);
methods.forEach(function (method) {
UncontrolledComponent.prototype[method] = function $proxiedMethod() {
var _inner;
return (_inner = this.inner)[method].apply(_inner, arguments);
};
});
UncontrolledComponent.ControlledComponent = Component;
/**
* useful when wrapping a Component and you want to control
* everything
*/
UncontrolledComponent.deferControlTo = function (newComponent, additions, nextMethods) {
if (additions === void 0) {
additions = {};
}
return uncontrollable(newComponent, _extends({}, controlledValues, additions), nextMethods);
};
return UncontrolledComponent;
}
exports.default = (0, _createUncontrollable2.default)(mixin, set);
module.exports = exports['default'];
module.exports = exports["default"];

4

package.json
{
"name": "uncontrollable",
"version": "4.1.0",
"version": "5.0.0",
"description": "Wrap a controlled react component, to allow specific prop/handler pairs to be uncontrolled",

@@ -21,3 +21,3 @@ "author": {

"peerDependencies": {
"react": ">=0.11.0"
"react": ">=15.0.0"
},

@@ -24,0 +24,0 @@ "jest": {

@@ -13,21 +13,15 @@ # uncontrollable

uncontrollable `3.0.0` comes with two versions of the utility, the "classic" one and a version that uses `batchedUpdates`, which solves some bugs related to update order, when used across multiple react roots, or "Portals". The batched version is the recommended version but it has one __major caveat__, since it uses a stateful addon, it _does not_ play well with global builds that rely on an externalized react file, such as from a CDN. If you are having problems with the batching version just use the normal one, it almost certainly will work for you.
If you are a bit unsure on the _why_ of this module read the next section first. If you just want to see some real-world examples, check out [React Widgets](https://github.com/jquense/react-widgets) which makes [heavy use of this strategy](https://github.com/jquense/react-widgets/blob/5d1b530cb094cdc72f577fe01abe4a02dd265400/src/Multiselect.jsx#L521).
```js
import classic from 'uncontrollable';
import batching from 'uncontrollable/batching';
import uncontrollable from 'uncontrollable'
```
Both versions have the same API.
### API
If you are a bit unsure on the _why_ of this module read the next section first. If you just want to see some real-world examples, check out [React Widgets](https://github.com/jquense/react-widgets) which makes [heavy use of this strategy](https://github.com/jquense/react-widgets/blob/5d1b530cb094cdc72f577fe01abe4a02dd265400/src/Multiselect.jsx#L521).
#### `uncontrollable(Component, propHandlerHash, [methods])`
- `Component`: is a valid react component, such as the result of `createClass`
- `propHandlerHash`: define the pairs of prop/handlers you want to be uncontrollable, e.g. `{ value: 'onChange'}`
- `methods`: since uncontrollable wraps your component in another component, methods are not immediately accessible. You can proxy them through by providing the names of the methods you want to continue to expose.
* `Component`: is a valid react component, such as the result of `createClass`
* `propHandlerHash`: define the pairs of prop/handlers you want to be uncontrollable, e.g. `{ value: 'onChange'}`
* `methods`: since uncontrollable wraps your component in another component, methods are not immediately accessible. You can proxy them through by providing the names of the methods you want to continue to expose.

@@ -37,11 +31,9 @@ For every prop you indicate as uncontrollable, the returned component will also accept an initial, `default` value for that prop. For example, `open` can be left uncontrolled but the initial value can be set via `defaultOpen={true}` if we want it to start open.

```js
var uncontrollable = require('uncontrollable');
const uncontrollable = require('uncontrollable')
var UncontrolledCombobox = uncontrollable(
Combobox,
{
value: 'onChange',
open: 'onToggle',
searchTerm: 'onSearch' //the current typed value (maybe it filters the dropdown list)
})
const UncontrolledCombobox = uncontrollable(Combobox, {
value: 'onChange',
open: 'onToggle',
searchTerm: 'onSearch', //the current typed value (maybe it filters the dropdown list)
})
```

@@ -55,3 +47,3 @@

```js
let UncontrolledForm = uncontrollable(Form, { value: 'onChange'}, ['submit'])
let UncontrolledForm = uncontrollable(Form, { value: 'onChange' }, ['submit'])

@@ -67,10 +59,12 @@ //when you use a ref this will work

```jsx
render: function() {
render() {
return (
<input type='text'
value={this.state.value}
onChange={ e => this.setState({ value: e.target.value })}/>
<input type='text'
value={this.state.value}
onChange={ e => this.setState({ value: e.target.value })}
/>
)
}
```
This pattern moves the responsibility of managing the `value` from the input to its parent and mimics "two-way" databinding. Sometimes, however, there is no need for the parent to manage the input's state directly. In that case, all we want to do is set the initial `value` of the input and let the input manage it from then on. React deals with this through "uncontrolled" inputs, where if you don't indicate that you want to control the state of the input externally via a `value` prop it will just do the book-keeping for you.

@@ -81,5 +75,4 @@

```js
var SimpleDropdown = React.createClass({
propTypes: {
class SimpleDropdown extends React.Component {
static propTypes = {
value: React.PropTypes.string,

@@ -89,5 +82,5 @@ onChange: React.PropTypes.func,

onToggle: React.PropTypes.func,
},
}
render: function() {
render() {
return (

@@ -97,17 +90,17 @@ <div>

value={this.props.value}
onChange={ e => this.props.onChange(e.target.value)}
onChange={e => this.props.onChange(e.target.value)}
/>
<button onClick={ e => this.props.onToggle(!this.props.open)}>
<button onClick={e => this.props.onToggle(!this.props.open)}>
open
</button>
{ this.props.open &&
<ul className='open'>
{this.props.open && (
<ul className="open">
<li>option 1</li>
<li>option 2</li>
</ul>
}
)}
</div>
)
}
});
}
```

@@ -124,14 +117,14 @@

```js
var uncontrollable = require('uncontrollable');
const uncontrollable = require('uncontrollable');
var UncontrollableDropdown = uncontrollable(SimpleDropdown, {
value: 'onChange',
open: 'onToggle'
})
const UncontrollableDropdown = uncontrollable(SimpleDropdown, {
value: 'onChange',
open: 'onToggle'
})
<UncontrollableDropdown
value={this.state.val} // we can still control these props if we want
onChange={val => this.setState({ val })}
defaultOpen={true} /> // or just let the UncontrollableDropdown handle it
// and we just set an initial value (or leave it out completely)!
<UncontrollableDropdown
value={this.state.val} // we can still control these props if we want
onChange={val => this.setState({ val })}
defaultOpen={true} /> // or just let the UncontrollableDropdown handle it
// and we just set an initial value (or leave it out completely)!
```

@@ -138,0 +131,0 @@

@@ -1,26 +0,15 @@

'use strict';
"use strict";
exports.__esModule = true;
exports.version = undefined;
exports.uncontrolledPropTypes = uncontrolledPropTypes;
exports.getType = getType;
exports.getValue = getValue;
exports.getLinkName = getLinkName;
exports.isProp = isProp;
exports.defaultKey = defaultKey;
exports.chain = chain;
exports.transform = transform;
exports.each = each;
exports.has = has;
exports.isReactComponent = isReactComponent;
var _react = require('react');
var _invariant = _interopRequireDefault(require("invariant"));
var _react2 = _interopRequireDefault(_react);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _invariant = require('invariant');
var noop = function noop() {};
var _invariant2 = _interopRequireDefault(_invariant);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function readOnlyPropType(handler, name) {

@@ -30,3 +19,3 @@ return function (props, propName) {

if (!props[handler]) {
return new Error('You have provided a `' + propName + '` prop to ' + '`' + name + '` without an `' + handler + '` handler. This will render a read-only field. ' + 'If the field should be mutable use `' + defaultKey(propName) + '`. Otherwise, set `' + handler + '`');
return new Error("You have provided a `" + propName + "` prop to `" + name + "` " + ("without an `" + handler + "` handler prop. This will render a read-only field. ") + ("If the field should be mutable use `" + defaultKey(propName) + "`. ") + ("Otherwise, set `" + handler + "`."));
}

@@ -37,32 +26,17 @@ }

function uncontrolledPropTypes(controlledValues, basePropTypes, displayName) {
function uncontrolledPropTypes(controlledValues, displayName) {
var propTypes = {};
Object.keys(controlledValues).forEach(function (prop) {
// add default propTypes for folks that use runtime checks
propTypes[defaultKey(prop)] = noop;
if (process.env.NODE_ENV !== 'production' && basePropTypes) {
transform(controlledValues, function (obj, handler, prop) {
(0, _invariant2.default)(typeof handler === 'string' && handler.trim().length, 'Uncontrollable - [%s]: the prop `%s` needs a valid handler key name in order to make it uncontrollable', displayName, prop);
obj[prop] = readOnlyPropType(handler, displayName);
}, propTypes);
}
if (process.env.NODE_ENV !== 'production') {
var handler = controlledValues[prop];
!(typeof handler === 'string' && handler.trim().length) ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, 'Uncontrollable - [%s]: the prop `%s` needs a valid handler key name in order to make it uncontrollable', displayName, prop) : invariant(false) : void 0;
propTypes[prop] = readOnlyPropType(handler, displayName);
}
});
return propTypes;
}
var version = exports.version = _react2.default.version.split('.').map(parseFloat);
function getType(component) {
if (version[0] >= 15 || version[0] === 0 && version[1] >= 13) return component;
return component.type;
}
function getValue(props, name) {
var linkPropName = getLinkName(name);
if (linkPropName && !isProp(props, name) && isProp(props, linkPropName)) return props[linkPropName].value;
return props[name];
}
function isProp(props, prop) {

@@ -72,38 +46,5 @@ return props[prop] !== undefined;

function getLinkName(name) {
return name === 'value' ? 'valueLink' : name === 'checked' ? 'checkedLink' : null;
}
function defaultKey(key) {
return 'default' + key.charAt(0).toUpperCase() + key.substr(1);
}
function chain(thisArg, a, b) {
return function chainedFunction() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
a && a.call.apply(a, [thisArg].concat(args));
b && b.call.apply(b, [thisArg].concat(args));
};
}
function transform(obj, cb, seed) {
each(obj, cb.bind(null, seed = seed || (Array.isArray(obj) ? [] : {})));
return seed;
}
function each(obj, cb, thisArg) {
if (Array.isArray(obj)) return obj.forEach(cb, thisArg);
for (var key in obj) {
if (has(obj, key)) cb.call(thisArg, obj[key], key, obj);
}
}
function has(o, k) {
return o ? Object.prototype.hasOwnProperty.call(o, k) : false;
}
/**

@@ -117,4 +58,6 @@ * Copyright (c) 2013-present, Facebook, Inc.

*/
function isReactComponent(component) {
return !!(component && component.prototype && component.prototype.isReactComponent);
}
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