Как обрабатывать события click на дочерних компонентах в React.js, есть ли "способ" React?

Я пытаюсь создать/прототип виджета с фильтром поиска и 2-панелями, которые имеют отношения key-value/parent-child. Правая панель содержит категории, у левой панели есть интересы, связанные с отдельной категорией. Строки категории можно выбрать, чтобы отображать связанные флажки. Я не стал пытаться понять, как обновить представление формы с помощью флажков, сначала мне нужно больше узнать о правильном способе создания потока данных.

Это мой первый набег на React, и я знаю, что React поощряет односторонний поток данных. Я чувствую, что обновляю панель интересов неправильно, используя jQuery для обработки событий. Я хотел бы узнать некоторые альтернативные способы обновления панели интересов.

Из того, что я прочитал, чтобы обновить родителя (я считаю, что его называют владельцем в документах React), вам нужно определить обратные вызовы для компонентов child/ownee. Я был бы признателен за объяснение этого, а также если я неправильно использую интересы в штате против реквизита. Я пытался только иметь фильтр поиска, и флажки были частью состояния для виджета, потому что я читал, что данные с фильтрацией должны принадлежать реквизитам.

UPDATE: я изменил интересы, которые будут инициализированы в getDefaultProps, поэтому теперь я использую их как реквизиты. Я думаю, что это скорее Реакт, я прав? Я также изменил способ обновления интересов, изменив функцию handleCategoryRowClick. Я думаю, что я делаю то, что нужно больше, но все равно буду любить любой совет, который мог бы дать любой, у кого больше опыта использования React, спасибо.

Любая помощь будет принята с благодарностью!

/** @jsx React.DOM */

var InterestRow = React.createClass({
    render: function() {
        return (
            <div>
                <label>{this.props.interest.name}</label>
                <input type="checkbox" ref={this.props.key} value={this.props.key} />
            </div>
        )
   }
});

var InterestPanel = React.createClass({
    var interestPanel = this;
    var rows = [];

    render: function() {
        var rows = [];
        var i = 0;

        _(this.props.interests).each(function (interest) {
            rows.push(<InterestRow interest={interest} key={interest.value} />);
        });

        return (
            <form>{rows}</form>
        )
    }
});

var CategoryRow = React.createClass({
    render: function() {
        return (
            <li onClick={this.props.handleClick.bind(null, this.props.interests)}>{this.props.category}</li>
        )
    }
});

var CategoriesPanel = React.createClass({
render: function() {
    var categoriesPanel = this;
    var rows = [];
    var categories = [];
    var interests = [];
    var interestSet = [];
    var missedSet = [];
    var lastCategory = null;
    var category;
    var interest;
    var i = 0;

    _.each(categoriesPanel.props.data, function (datum) {
        name = datum.name;
        value = datum.targeting_value;
        category = name.split('/')[0];
        interest = {name: name.split('/')[1], value: value}

        if (_.contains(interest.name.toLowerCase(), categoriesPanel.props.searchText.toLowerCase())) {
            if (category !== lastCategory) {
                if (interestSet.length > 0) {
                    interests.push(interestSet.concat(missedSet));
                }

                lastCategory = category;
                categories.push(category);
                interestSet = [];
                interestSet.push(interest);
                missedSet = [];
            } else {
                if (!_.contains(categories, category)) {
                    categories.push(category);
                    interestSet.push(interest);
                } else {
                    interestSet.push(interest);
                }
            }
        } else {
            if (category !== lastCategory) {
                if (interestSet.length > 0) {
                    interests.push(interestSet.concat(missedSet));
                }

                lastCategory = category;
                interestSet = [];
                missedSet = [];
                missedSet.push(interest);
            } else {
                missedSet.push(interest);
            }
        }
    });

    if (interestSet.length > 0) {
        interests.push(interestSet.concat(missedSet));
    }

    var interestsObject = _.zipObject(categories, interests);

    _.each(interestsObject, function (interestSet, category) {
        i++;
        rows.push(<CategoryRow category={category} key={i} interests={interestSet} handleClick={categoriesPanel.props.handleClick} />)
    });

    return (
        <div>
            <ul>{rows}</ul>
        </div>
    )
}
});

var SearchBar = React.createClass({
    handleChange: function() {
        this.props.onUserInput(
            this.refs.searchTextInput.getDOMNode().value
        )
    },
    render: function() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input
                    type="text"
                    placeholder="Search Interests..."
                    value={this.props.searchText}
                    ref="searchTextInput"
                    onChange={this.handleChange}
                />
            </form>
        );
     }
});

var InterestsTable = React.createClass({
loadDataFromTwitter: function() {
    $.ajax({
        url: this.props.url,
        dataType: 'json',
        success: function(data) {
            this.setProps({data: data});
        }.bind(this)
    });
},
getInitialState: function() {
   return {
       searchText: ''
   }
},
getDefaultProps: function() {
   return {
       data: [],
       interests: []
   }
},
componentWillMount: function() {
    this.loadDataFromTwitter();
},
handleUserInput: function(searchText) {
   this.setState({
       searchText: searchText
   });
},
handleCategoryRowClick: function(interests) {
    this.setProps({
        interests: interests
    });
},
render: function() {
    return (
        <div>
            <SearchBar
                searchText={this.state.searchText}
                onUserInput={this.handleUserInput}
            />
            <CategoriesPanel
                data={this.props.data}
                searchText={this.state.searchText}
                handleClick={this.handleCategoryRowClick}
            />
            <InterestsPanel
                interests={this.props.interests}
                searchText={this.state.searchText}
            />
        </div>
    );
}
});

React.renderComponent(
     <InterestsTable url="/api/interests" />,
    document.getElementById('content')
);

Ответ 1

  • Есть несколько затяжных jQuery, которые можно удалить:
 
    componentDidMount: function() {
      $(document).on("handleCategoryRowClick", this.handleCategoryRowClick);
    },
  • Относитесь к props как можно более неизменному и реагируйте на жизнь циклические методы будут работать, как ожидалось. В этом случае interests должно быть state вместо props, потому что компонент InterestsTable принадлежит и изменяет его; он не передается родителем компонента:
    getInitialState: function() {
      return {
        interests: [],
        searchText: ""
      };
    },
    handleCategoryRowClick: function(e, interests) {
      this.setState({
        interests: interests
      });
    },