React(Facebook):受控复选框的托管状态

时间:2014-02-05 15:41:48

标签: javascript facebook reactjs

尝试使用React创建一个选中和取消选择其他单个复选框(选择/取消全选)的复选框时,我遇到了一些问题。我看过http://facebook.github.io/react/docs/forms.html并发现受控不受控制 <input>之间存在差异。我的测试代码如下:

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'));

“全局选择器”按预期工作:选中后,将选中所有其他检查。问题是,当单击其他复选框之一时,不会触发__changeSelection()处理程序。

我不知道做这项工作的正确方法是什么。也许React模型不是模拟这种交互的最佳模型?我该怎么办?

提前致谢

2 个答案:

答案 0 :(得分:43)

render函数中,this映射函数的checks范围与render不同,后者是__changeSelection所需的范围,因此this.__changeSelection无法找到__changeSelection属性。如果您在该映射函数的末尾添加.bind(this),则可以将其范围绑定到与this相同的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));

在旁注中,我只是将id传递给处理函数而不是分配数据属性。这将消除在处理程序中找到该元素的需要:

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

然后更新您的__changeSelection函数以传递id作为第一个arg并删除属性查找行:

__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 });

}

这是一个放在一起的例子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'));

答案 1 :(得分:3)

如果您正在处理复选框,则可以使用checkedLink属性。这是另一种可能的实现,它使全局复选框受到控制(而不是在当前答案中不受控制):

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>
        );
    },

});

如果要变异的状态没有深度嵌套,那么checkedLink=this.linkState("checkboxValue")LinkedStateMixin一起使用会更简单(就像这个问题中的情况一样)

修改checkedLinkvalueLink已被弃用,但在之前的React版本中受到推荐。