Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React (Facebook): managed state of controlled checkboxes

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

like image 907
Matheus Moreira Avatar asked Feb 05 '14 15:02

Matheus Moreira


People also ask

How do you unCheck a checkbox when another one is checked in React?

1- You can use ref with check boxes, and onClick of button, by using ref you can unCheck the box. 2- You can use controlled element, means store the status of check box inside a state variable and update that when button clicked.


1 Answers

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')); 
like image 83
Michael LaCroix Avatar answered Oct 13 '22 00:10

Michael LaCroix