I\'m working with a RadioButtonGroup component which is like radio input but with buttons:
I would not like to add onClick handler to every button element
React uses events delegation pattern, so you can freely adding as many onClick handlers as needs.
Another way, if you just want to make code clear, you may create your own button component, and pass _onActiveChange to it props, but i don't sure that it's necessary in your case.
And remember, that React works with synthetic events, and, in some cases, usage of setState within native event handlers may cause an unpredictable behaviour.
To get an api like this (similar to <input/>
), we need to use the cloneWithProps addon.
<RadioButtonGroup onChange={this.handleChange} value={this.state.selected}>
<button>Test 1</button>
<button>Test 2</button>
<button>Test 3</button>
</RadioButtonGroup>
All this does is take each child, add a click handler, and conditionally add a className of 'active' to it. You easily can (and should) modify it to take the active class name as a prop.
var RadioButtonGroup = React.createClass({
render: function(){
return <div>{React.Children.map(this.props.children, this.renderItem)}</div>
},
renderItem: function(button, index){
return React.cloneElement(button, {
className: this.props.value === index ? ' active ' : '',
onClick: function(){
this.props.onChange(index);
}.bind(this),
key: index
});
}
});
If you don't want to use cloneWithProps, you could use a wrapper div, but styling may be a bit more complex.
renderItem: function(button, index){
return React.createElement('div', {
className: this.props.value === index ? ' active ' : '',
onClick: function(){
this.props.onChange(index);
}.bind(this),
key: index
}, button);
}
The reason everything uses index is because you're passing react elements, which are opaque. There's no clean way to get any data out of these buttons, but we do know their index because we're iterating over them using React.Children.map. An alternative api would look like this:
<RadioButtonGroup value={'test1'} onChange={fn} options={{
test1: <button>Test 1</button>,
test2: <button>Test 2</button>,
test3: <button>Test 3</button>
}} />
Here we can iterate over this.props.options, and pass the key to the onChange callback, and take e.g. 'test1' as a value prop.
This is my code .I do not know whether this is the best way ,as i started using react just 5 hrs ago. But it works fine.
var LeftPane = React.createClass({
getInitialState: function() {
return {list:[{name:"Item 1",isactive:1},
{name:"Item 2",isactive:0},
{name:"Item 3",isactive:0},
{name:"Item 4",isactive:0},
{name:"Item 5",isactive:0}]};
},
clickHandler: function(index) {
var current_list = this.state.list;
current_list.map(function(record,i){
if(record.isactive==1){
if(i!=index){
record.isactive =0;
}else{
return;
}
}else{
if(i==index){
record.isactive =1;
}
}
});
this.setState({data:current_list});
},
render: function() {
var active_item = "list-group-item active"
var non_active_item ="list-group-item";
var _this = this;
return (
<div className="list-group">
{this.state.list.map(function(record,i) {
if(record.isactive==1){
return <a className={active_item} onClick={_this.clickHandler.bind(null, i)}>{record.name}</a>
}else{
return <a className={non_active_item} onClick={_this.clickHandler.bind(null, i)}>{record.name}</a>
}
})}
</div>
</div>
)
}
});
You don't want to mess with click handlers on each button, just listen for the click on the container. Then update the state based on which child is clicked.
Also, with React it's best to keep all of your DOM stuff in the render function. In this case, defining an element's class name.
Here's how this could work:
var RadioButtonGroup = React.createClass({
getInitialState: function getInitialState() {
return {
active: this.props.active || 0
};
},
clickHandler: function clickHandler(e) {
// Getting an array of DOM elements
// Then finding which element was clicked
var nodes = Array.prototype.slice.call( e.currentTarget.children );
var index = nodes.indexOf( e.target );
this.setState({ active: index });
},
render: function render() {
var buttons = this.children.map(function(child, i) {
if (i === this.state.active) child.props.className += ' active';
return child;
}, this);
return (
<div className="radio-button-group" onClick={ this.clickHandler }>
{ buttons }
</div>
)
}
});