我正在使用RadioButtonGroup组件,它类似于无线电输入,但带有按钮:
如果使用组件很容易就像这样:
var SelectThing = React.createClass({
render: function render() {
// I would not like to add onClick handler to every button element
// outside the RadioButtonGroup component
return (
<RadioButtonGroup onChange={this._onActiveChange}>
<button>Thing 1</button>
<button>Thing 2</button>
<button>Thing 3</button>
</RadioButtonGroup>
)
},
_onActiveChange: function _onActiveChange(index) {
console.log('You clicked button with index:', index);
}
});
Run Code Online (Sandbox Code Playgroud)
实际问题: 如何用React实现最优雅的效果?(我发现了另一个类似的问题,但它并没有完全回答这个问题).
我的第一个直觉是在组件内添加和删除onClick处理程序,以从组件的用户中删除锅炉板代码.我想到的另一个选择是给出<p>
元素而不是按钮元素,并将它们放在将在RadioButtonGroup组件内创建的按钮元素中.我不太喜欢后者,因为与传递按钮相比,它在语义上没有那么多意义.
这是(显然不起作用)组件现在的样子:
// Radio input with buttons
// http://getbootstrap.com/javascript/#buttons-checkbox-radio
var RadioButtonGroup = React.createClass({
getInitialState: function getInitialState() {
return {
active: this.props.active || 0
};
},
componentWillMount: function componentWillMount() {
var buttons = this.props.children;
buttons[this.props.active].className += ' active';
var self = this;
buttons.forEach(function(button, index) {
// How to dynamically set onclick handler for virtual dom
// element created inside JSX?
button.addEventListener('onClick', function(event) {
self._onAnyButtonClick(index, event);
}
});
},
componentWillUnmount: function componentWillUnmount() {
var buttons = this.props.children;
buttons.forEach(function(button, index) {
button.removeEventListener('onClick');
});
},
render: function render() {
return (
<div className="radio-button-group">
{buttons}
</div>
)
},
_onAnyButtonClick: function _onAnyButtonClick(index, event) {
this.setState({
active: index
});
this.props.onChange(index);
}
});
Run Code Online (Sandbox Code Playgroud)
小智 20
你不想在每个按钮上弄乱点击处理程序,只需要听取容器上的点击.然后根据单击的子项更新状态.
另外,使用React最好将所有DOM内容保存在render函数中.在这种情况下,定义元素的类名.
这是如何工作的:
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>
)
}
});
Run Code Online (Sandbox Code Playgroud)
为了获得这样的api(类似于<input/>
),我们需要使用cloneWithProps插件.
<RadioButtonGroup onChange={this.handleChange} value={this.state.selected}>
<button>Test 1</button>
<button>Test 2</button>
<button>Test 3</button>
</RadioButtonGroup>
Run Code Online (Sandbox Code Playgroud)
所有这一切都是为每个孩子,添加一个单击处理程序,并有条件地添加一个className为'active'.您可以(并且应该)修改它以将活动类名称作为道具.
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
});
}
});
Run Code Online (Sandbox Code Playgroud)
如果您不想使用cloneWithProps,则可以使用包装器div,但样式可能会更复杂一些.
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);
}
Run Code Online (Sandbox Code Playgroud)
一切都使用索引的原因是因为你传递的反应元素是不透明的.没有干净的方法从这些按钮中获取任何数据,但我们知道他们的索引,因为我们使用React.Children.map迭代它们.另一种api看起来像这样:
<RadioButtonGroup value={'test1'} onChange={fn} options={{
test1: <button>Test 1</button>,
test2: <button>Test 2</button>,
test3: <button>Test 3</button>
}} />
Run Code Online (Sandbox Code Playgroud)
在这里,我们可以迭代this.props.options,并将密钥传递给onChange回调,并将例如'test1'作为值prop.
归档时间: |
|
查看次数: |
31815 次 |
最近记录: |