React (Facebook): managed state of controlled checkboxes React (Facebook): managed state of controlled checkboxes reactjs reactjs

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):

JsFiddle

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.