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 1.2.0 to 1.3.0

48

lib/uncontrollable.js
"use strict";
var babelHelpers = require("./util/babelHelpers.js");
var React = require("react");
var ReactUpdates = require("react/lib/ReactUpdates");
var invariant = require("react/lib/invariant");

@@ -30,2 +31,10 @@

function forceUpdateIfMounted() {
if (this.isMounted() && this._needsUpdate) {
this._needsUpdate = false;
this.forceUpdate();
}
}
module.exports = function (Component, controlledValues, taps) {

@@ -58,3 +67,7 @@ var name = Component.displayName || Component.name || "Component",

getInitialState: function () {
componentWillMount: function () {
var _this = this;
this.values = Object.create(null);
var props = this.props,

@@ -64,9 +77,8 @@ keys = Object.keys(controlledValues);

return transform(keys, function (state, key) {
state[key] = props[defaultKey(key)];
_this.values[key] = props[defaultKey(key)];
}, {});
},
shouldComponentUpdate: function () {
//let the setState trigger the update
return !this._notifying;
componentWillReceiveProps: function (nextProps) {
this._needsUpdate = false;
},

@@ -91,3 +103,3 @@

newProps[propName] = prop !== undefined ? prop : _this.state[propName];
newProps[propName] = prop !== undefined ? prop : _this.values[propName];

@@ -99,3 +111,2 @@ newProps[handle] = setAndNotify.bind(_this, propName);

//console.log('props: ', newProps)
each(taps, function (val, key) {

@@ -116,21 +127,13 @@ return newProps[key] = chain(_this, val, newProps[key]);

handler = this.props[controlledValues[propName]];
//, controlled = handler && isProp(this.props, propName);
if (linkName && isProp(this.props, linkName) && !handler) {
handler = this.props[linkName].requestChange
//propName = propName === 'valueLink' ? 'value' : 'checked'
;
handler = this.props[linkName].requestChange;
}
if (handler) {
this._notifying = true;
handler.call.apply(handler, [this, value].concat(args));
this._notifying = false;
}
if (handler) handler.call.apply(handler, [this, value].concat(args));
this.setState((function () {
var _setState = {};
_setState[propName] = value;
return _setState;
})());
this.values[propName] = value;
this._needsUpdate = true;
ReactUpdates.asap(forceUpdateIfMounted, this);
}

@@ -171,3 +174,2 @@

return o ? Object.prototype.hasOwnProperty.call(o, k) : false;
}
//return !controlled
}
{
"name": "uncontrollable",
"version": "1.2.0",
"version": "1.3.0",
"description": "Wrap a controlled react component, to allow spcific prop/handler pairs to be uncontrolled",

@@ -50,2 +50,3 @@ "author": {

"react-hot-loader": "^1.1.7",
"react-layer": "^1.1.0",
"rimraf": "^2.2.8",

@@ -52,0 +53,0 @@ "sinon-chai": "^2.6.0",

@@ -21,3 +21,2 @@ # uncontrollable

- `propHandlerHash`: define the pairs of prop/handlers you want to be uncontrollable eg. `{ value: 'onChange'}`
- `tapsHash`: sometimes you need to jump in the middle of a handler to adjust the value of another prop. You can specify a tap function to run before the handler like: `{ onToggle: function(){ this.setState({value: null }) }`. `this` in the tab will be the uncontrolled component instance, you can use it to adjust its own internal state (`setState` calls will not trigger duplicate renders). This is generally not recommended if it can be avoided, but sometimes it is necessary to deal with interactions of multiple controlled/uncontrolled pairs in a component.

@@ -32,11 +31,5 @@ For ever 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.

{
value: 'onChange',
value: ['onChange', (e, current) => !v,
open: 'onToggle',
searchTerm: 'onSearch' //the current typed value (maybe it filters the dropdown list)
},
{
'onToggle': function (isOpen){
if ( !isOpen && this.props.searchTerm === undefined ) // if the consumer is not controlling searchTerm
this.setState({ searchTerm: '' }) // reset the filter on close
}
})

@@ -43,0 +36,0 @@ ```

@@ -30,4 +30,6 @@ 'use strict';

global.expect = chai.expect
var testsContext = require.context("./test", true);
testsContext.keys().forEach(testsContext);
'use strict';
var React = require('react/addons')
var uncontrol = require('../src/uncontrollable')
var Layer = require('react-layer')

@@ -28,13 +29,19 @@ var TestUtils = React.addons.TestUtils

onToggle: React.PropTypes.func,
onRender: React.PropTypes.func,
},
render() {
if ( this.props.onRender )
this.props.onRender(this.props)
return (
<div>
<button onClick={this.props.onToggle}>toggle</button>
{ this.props.open &&
{ this.props.open &&
<span className='open'>open!</span>
}
<input className='valueInput'
value={this.props.value}
value={this.props.value}
onChange={ e => this.props.onChange(e.value)}/>

@@ -54,3 +61,3 @@ <input type='checkbox'

, instance = render(<Control value={3}/>)
warn.should.have.been.CalledOnce;

@@ -70,3 +77,3 @@

, input = findAllTag(instance, 'input')[0]
input.getDOMNode().value.should.equal('10')

@@ -84,3 +91,3 @@

, input = findAllTag(instance, 'input')[1]
input.getDOMNode().checked.should.equal(false)

@@ -107,10 +114,10 @@

it('should track state if no specified', () => {
it('should track internally if not specified', () => {
var Control = uncontrol(Base, { value: 'onChange' })
, instance = render(<Control />)
, input = findAllTag(instance, 'input')[0]
trigger.change(input.getDOMNode(), { value: 42})
instance.state.should.have.property('value')
expect(instance.values).to.have.property('value')
.that.equals(42)

@@ -124,3 +131,3 @@ })

, span = findClass(instance, 'open')
input.getDOMNode().value.should.equal('10')

@@ -130,5 +137,106 @@

instance.state.value.should.equal(42)
expect(instance.values.value).to.equal(42)
})
it('should update in the right order when controlled', () => {
var Control = uncontrol(Base, { value: 'onChange' })
, spy = sinon.spy();
var Parent = React.createClass({
getInitialState(){ return { value: 5 } },
render(){
return (
<Control
onRender={spy}
value={this.state.value}
onChange={value => this.setState({ value })}
/>
)
}
})
var instance = render(<Parent/>)
, input = findAllTag(instance, 'input')[0]
trigger.change(input.getDOMNode(), { value: 42 })
spy.callCount.should.equal(2)
spy.firstCall.args[0].value.should.equal(5)
spy.secondCall.args[0].value.should.equal(42)
})
it('should update in the right order when uncontrolled', () => {
var Control = uncontrol(Base, { value: 'onChange' })
, spy = sinon.spy();
var Parent = React.createClass({
getInitialState(){ return { value: 5 } },
render(){
return (
<Control
onRender={spy}
defaultValue={this.state.value}
/>
)
}
})
var instance = render(<Parent/>)
, input = findAllTag(instance, 'input')[0]
trigger.change(input.getDOMNode(), { value: 42 })
spy.callCount.should.equal(2)
spy.firstCall.args[0].value.should.equal(5)
spy.secondCall.args[0].value.should.equal(42)
})
it('should update correctly in a Layer', () => {
var Control = uncontrol(Base, { value: 'onChange' })
, spy = sinon.spy();
var Parent = React.createClass({
getInitialState(){ return { value: 5 } },
componentWillUnmount () {
this._layer.destroy()
this._layer = null
},
componentDidUpdate(){this._renderOverlay()},
componentDidMount() {this._renderOverlay()},
_renderOverlay() {
if (!this._layer)
this._layer = new Layer(document.body, ()=> this._child)
this.layerInstance = this._layer.render()
},
render(){
this._child = (
<Control
onRender={spy}
value={this.state.value}
onChange={value => this.setState({ value, called: true })}
/>
)
return (
<div/>
)
}
})
var instance = render(<Parent/>)
, input = findAllTag(instance.layerInstance, 'input')[0]
trigger.change(input.getDOMNode(), { value: 42 })
spy.callCount.should.equal(2)
spy.firstCall.args[0].value.should.equal(5)
spy.secondCall.args[0].value.should.equal(42)
})
describe('taps', () => {

@@ -162,2 +270,1 @@

})
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