How to handle click events on child components in React.js, is there a React "way"?

StackOverflow https://stackoverflow.com/questions/23485242

  •  16-07-2023
  •  | 
  •  

質問

I'm attempting to create/prototype a widget that has a search filter and 2-panels that have a key-value/parent-child relationship. The right panel contains categories, the left panel has interests that are associated with an individual category. The category rows are selectable to show the associated interest checkboxes. I haven't gotten to the point of trying to understand how to update the form submission with the checkboxes, first I need to understand more about the correct way to make the data flow.

This is my first foray into React and I know that React encourages one-way data flow. I feel that I am updating the interests panel incorrectly by using jQuery to handle the events. I'd like to know some alternative ways to update the interests panel.

From what I've read, in order to update parent (I believe its referred to as owner in the React docs) components you need to define callbacks on the child/ownee components. I'd appreciate an explanation of this and also if I'm incorrectly using interests in state vs props. I was attempting to only have the Search filter & the checkboxes be a part of the state for the widget, because I read that filterable data should belong in props.

UPDATE: I changed the interests to be initialized in getDefaultProps, so now I'm using them as props. I think this is more the React way, am I correct? I also changed the way that interests are updated by changing the handleCategoryRowClick function. I think I am doing things the more React way now, but would still love any advice that anyone with more experience using React could give, thank you.

Any help would be greatly appreciated!

/** @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')
);
役に立ちましたか?

解決

  • There is some lingering jQuery that can be removed:
    componentDidMount: function() {
      $(document).on("handleCategoryRowClick", this.handleCategoryRowClick);
    },
  • Treat props as immutable as often as possible and React life cycle methods will work as expected. In this case, interests should be state instead of props because the InterestsTable component owns and modifies it; it's not passed in by the component's parent:
    getInitialState: function() {
      return {
        interests: [],
        searchText: ""
      };
    },
    handleCategoryRowClick: function(e, interests) {
      this.setState({
        interests: interests
      });
    },
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top