Подтвердить что ты не робот

React (Facebook): управляемое состояние контролируемых флажков

У меня возникла небольшая проблема при попытке создать флажок, который выбирает и отменяет выделение других отдельных флажков (выберите/отмените выбор всех) с помощью React. Я прочитал http://facebook.github.io/react/docs/forms.html и обнаружил, что существуют различия между "контролируемыми" и "неконтролируемыми" <input> s. Мой тестовый код выглядит следующим образом:

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 не самая лучшая модель для такого рода взаимодействия? Что я могу сделать?

Заранее спасибо

4b9b3361

Ответ 1

В вашей функции 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 в качестве первого аргумента и удалите строку поиска атрибутов:

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

}

Вот пример всего этого: вместе с jsfiddle для вас попробовать:

/** @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'));

Ответ 2

Если вы имеете дело с флажками, вы можете использовать атрибут 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, если состояние мутировать не глубоко вложенное (например, в этом случае)

Изменить: checkedLink и valueLink устарели, но были рекомендованы в предыдущих версиях React.