I'm building a custom ReactJS component (A 'Select2' dropdown library wrapper) and want to implement support for the standard two-way binding helper with the 'valueLink' parameter.
However it appears the mixin to handle the 'valueLink' parameter only applies to standard components, not custom components.
Is there a way to have my component implement the standard valueLink behaviour automatically, or will I need to explicitly parse and implement this support myself (Potentially introducing bugs or odd behaviours that aren't present in the base library)
The object returned from this.linkState
when using the LinkedStateMixin
has two relevant properties: value
and requestChange()
. Simply use those two properties as your value and change handler, respectively, just as if they had been passed to your custom component via value
and onChange
.
Here's an example of a composite component that wraps a jQuery color picker; it works both with valueLink
and with standard value
and onChange
properties. (To run the example, expand the "Show code snippet" then click "Run code snippet" at the bottom.)
var ColorPicker = React.createClass({
render: function() {
return <div />;
},
getValueLink: function(props) {
// Create an object that works just like the one
// returned from `this.linkState` if we weren't passed
// one; that way, we can always behave as if we're using
// `valueLink`, even if we're using plain `value` and `onChange`.
return props.valueLink || {
value: props.value,
requestChange: props.onChange
};
},
componentDidMount: function() {
var valueLink = this.getValueLink(this.props);
jQuery(this.getDOMNode()).colorPicker({
pickerDefault: valueLink.value,
onColorChange: this.onColorChange
});
},
componentWillReceiveProps: function(nextProps) {
var valueLink = this.getValueLink(nextProps);
var node = jQuery(this.getDOMNode());
node.val(valueLink.value);
node.change();
},
onColorChange: function(id, color) {
this.getValueLink(this.props).requestChange(color);
}
});
div.colorPicker-picker {
height: 16px;
width: 16px;
padding: 0 !important;
border: 1px solid #ccc;
background: url(https://raw.github.com/laktek/really-simple-color-picker/master/arrow.gif) no-repeat top right;
cursor: pointer;
line-height: 16px;
}
div.colorPicker-palette {
width: 110px;
position: absolute;
border: 1px solid #598FEF;
background-color: #EFEFEF;
padding: 2px;
z-index: 9999;
}
div.colorPicker_hexWrap {width: 100%; float:left }
div.colorPicker_hexWrap label {font-size: 95%; color: #2F2F2F; margin: 5px 2px; width: 25%}
div.colorPicker_hexWrap input {margin: 5px 2px; padding: 0; font-size: 95%; border: 1px solid #000; width: 65%; }
div.colorPicker-swatch {
height: 12px;
width: 12px;
border: 1px solid #000;
margin: 2px;
float: left;
cursor: pointer;
line-height: 12px;
}
<script src="http://fb.me/react-with-addons-0.11.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.11.2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="https://dl.dropboxusercontent.com/u/113308/dnd/jsfiddle/jquery.colorPicker.min.js"></script>
<p><strong>With valueLink</strong></p>
<div id="app1"></div>
<hr>
<p><strong>With value and onChange</strong></p>
<div id="app2"></div>
<script type="text/jsx">
/** @jsx React.DOM */
var ApplicationWithValueLink = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return { color: "#FF0000" }
},
render: function() {
return (
<div>
<div>
<span style={{color: this.state.color}}>My Color Picker</span>
<button onClick={this.changeColor.bind(null, "#FF0000")}>Red</button>
<button onClick={this.changeColor.bind(null, "#00FF00")}>Green</button>
<button onClick={this.changeColor.bind(null, "#0000FF")}>Blue</button>
<input type="text" valueLink={this.linkState("color")} />
</div>
<div>
<ColorPicker valueLink={this.linkState("color")} />
</div>
</div>
);
},
changeColor: function(color) {
this.setState({color: color});
}
});
var ApplicationWithoutValueLink = React.createClass({
getInitialState: function() {
return { color: "#FF0000" }
},
render: function() {
return (
<div>
<div>
<span style={{color: this.state.color}}>My Color Picker</span>
<button onClick={this.changeColor.bind(null, "#FF0000")}>Red</button>
<button onClick={this.changeColor.bind(null, "#00FF00")}>Green</button>
<button onClick={this.changeColor.bind(null, "#0000FF")}>Blue</button>
<input type="text" value={this.state.color} onChange={this.changeColorText} />
</div>
<div>
<ColorPicker value={this.state.color} onChange={this.changeColor} />
</div>
</div>
);
},
changeColor: function(color) {
this.setState({color: color});
},
changeColorText: function(evt) {
this.changeColor(evt.target.value);
}
});
var ColorPicker = React.createClass({
render: function() {
return (
<div />
);
},
getValueLink: function(props) {
return props.valueLink || {
value: props.value,
requestChange: props.onChange
};
},
componentDidMount: function() {
var valueLink = this.getValueLink(this.props);
jQuery(this.getDOMNode()).colorPicker({
pickerDefault: valueLink.value,
onColorChange: this.onColorChange
});
},
componentWillReceiveProps: function(nextProps) {
var valueLink = this.getValueLink(nextProps);
var node = jQuery(this.getDOMNode());
node.val(valueLink.value);
node.change();
},
onColorChange: function(id, color) {
this.getValueLink(this.props).requestChange(color);
}
});
React.renderComponent(<ApplicationWithValueLink />, document.getElementById("app1"));
React.renderComponent(<ApplicationWithoutValueLink />, document.getElementById("app2"));
</script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With