React (Facebook): managed state of controlled checkboxes
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'));
If you are dealing with checkboxes you can use the checkedLink
attribute. Here is another possible implementation, that makes the global checkbox controlled (instead of uncontrolled in the current answers):
var Test = React.createClass({ getInitialState: function() { return { globalCheckbox: false, data: [ { id: 1, selected: false }, { id: 2, selected: false }, { id: 3, selected: false }, { id: 4, selected: false } ] }; }, changeCheckForId: function(id,bool) { this.setState( { data: this.state.data.map(function(d) { var newSelected = (d.id === id ? bool : d.selected); return {id: d.id, selected: newSelected}; } )}); }, changeCheckForAll: function(bool) { this.setState({ globalCheckbox: true, data: this.state.data.map(function(d) { return {id: d.id, selected: bool}; }) }); }, linkCheckbox: function(d) { return { value: d.selected, requestChange: function(bool) { this.changeCheckForId(d.id,bool); }.bind(this) }; }, linkGlobalCheckbox: function() { return { value: this.state.globalCheckbox, requestChange: function(bool) { this.changeCheckForAll(bool); }.bind(this) }; }, render: function() { var checks = this.state.data.map(function(d) { return ( <div> <input key={d.id} type="checkbox" checkedLink={this.linkCheckbox(d)} /> {d.id} <br /> </div> ); }.bind(this)); return ( <form> <input type="checkbox" checkedLink={this.linkGlobalCheckbox()} />Global selector <br /> {checks} </form> ); },});
It is simpler to use checkedLink=this.linkState("checkboxValue")
with LinkedStateMixin
if the state to mutate is not deeply nested (like this is the case in this question)
Edit: checkedLink
and valueLink
are being deprecated but were recommmended in previous versions of React.