React (Facebook): managed state of controlled checkboxes
facebook, javascript, reactjs
Solution
In your `render` function, the scope of `this` for the `checks` mapping function is different from `render`, which is the scope you need for `__changeSelection`, so `this.__changeSelection` won't locate a `__changeSelection` property. If you add a `.bind(this)` to the end of that mapping function, you can bind it's scope to the same `this` as `render`:
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" data-id={d.id} checked={d.selected} onChange={this.__changeSelection} />
{d.id}
<br />
</div>
);
}.bind(this));
On a side note, I would just pass the `id` to the handler function instead of assigning data-attributes. This will remove the need to locate that element in your handler:
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
{d.id}
<br />
</div>
);
}.bind(this));
Then update your `__changeSelection` function to pass in the `id` as the first arg and remove the attribute lookup line:
__changeSelection: function(id) {
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
}
Here is an example of it all put together, along with a jsfiddle for you to try it out:
/** @jsx React.DOM */
var Test = React.createClass({
getInitialState: function() {
return {
data: [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false }
]
};
},
render: function() {
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
{d.id}
<br />
</div>
);
}.bind(this));
return (
<form>
<input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks} />Global selector
<br />
{checks}
</form>
);
},
__changeSelection: function(id) {
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
},
__changeAllChecks: function() {
var value = this.refs.globalSelector.getDOMNode().checked;
var state = this.state.data.map(function(d) {
return { id: d.id, selected: value };
});
this.setState({ data: state });
}
});
React.renderComponent(<Test />, document.getElementById('content'));
Problem
I'm having a little problem while trying to create a checkbox that selects and deselects other individual checkboxes (select/deselect all) with React. I've read http://facebook.github.io/react/docs/forms.html and discovered that there are differences between controlled and not-controlled `<input>`s. My test code is as follows: ``` var Test = React.createClass({ getInitialState: function() { return { data: [ { id: 1, selected: false }, { id: 2, selected: false }, { id: 3, selected: false }, { id: 4, selected: false } ] }; }, render: function() { var checks = this.state.data.map(function(d) { return ( <div> <input type="checkbox" data-id={d.id} checked={d.selected} onChange={this.__changeSelection} /> {d.id} <br /> </div> ); }); return ( <form> <input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks} />Global selector <br /> {checks} </form> ); }, __changeSelection: function(e) { var id = e.target.getAttribute('data-id'); var state = this.state.data.map(function(d) { return { id: d.id, selected: (d.id === id ? !d.selected : d.selected) }; }); this.setState({ data: state }); }, __changeAllChecks: function(e) { var value = this.refs.globalSelector.getDOMNode().checked; var state = this.state.data.map(function(d) { return { id: d.id, selected: value }; }); this.setState({ data: state }); } }); React.renderComponent(<Test />, document.getElementById('content')); ``` The "Global selector" works as expected: when selected, all other checks are selected. The problem is that the `__changeSelection()` handler is not fired when one of the other checkboxes are clicked. I don't know what is the proper way to make this work. Maybe React model is not the best one to model this kind of interaction? What could I do? Thanks in advance