Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom ReactJS component that respects 'valueLink'

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)

like image 899
James Davies Avatar asked Sep 23 '14 02:09

James Davies


1 Answers

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>
like image 51
Michelle Tilley Avatar answered Sep 21 '22 10:09

Michelle Tilley