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')
);
To pass an onChange event handler to a child component in React: Define the event handler function in the parent component. Pass it as a prop to the child component, e.g. <Child handleChange={handleChange} /> . Set it to the onChange prop on the input field in the child.
We can use the createRef() method to create a reference for any element in the class-based component. Then we can check whether click event occurred in the component or outside the component. In the functional component, we can use the useRef() hook to create a reference for any element.
Adding EventsReact events are written in camelCase syntax: onClick instead of onclick . React event handlers are written inside curly braces: onClick={shoot} instead of onClick="shoot()" .
componentDidMount: function() {
$(document).on("handleCategoryRowClick", this.handleCategoryRowClick);
},
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
});
},
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With