随着应用规模的扩大,应该考虑对相同功能进行抽象,以提高代码的复用率和开发效率。React 表单组件中,我们可以对多个表单元素共享事件处理器,或将功能相同的组件编写为自定义组件。
1. 多表单元素共享事件处理器
在创建表单组件时,有时多个组件的事件处理器功能相同或相似,这时我们可以在多个组件间共享一个事件处理器,而不用分别编写事件处理器。共享事件处理器时,需要解决参数传递的问题。以下几种传递参数的方法:
1.1 通过.bind()方法共享
.bind()方法会为指定对象创建一个函数的实例,调用.bind()方法的时可以传递参数。
如,我们可以通过.bind()在多个组件中共享change事件的事件处理器,并根据组件的不同传递不同的参数:
var MyForm = React.createClass({
getInitialState: function() {
return {name: 'IT笔录',
domain: 'niefengjun.cn'};
},
handleChange: function(name, event) {
var newState = {};
newState[name] = event.target.value;
this.setState(newState);
},
handleSubmit: function(event){
event.preventDefault();
alert(this.state.name+'-'+this.state.domain)
},
render: function() {
return (<form onSubmit={this.handleSubmit}>
<label htmlFor="name">名称:</label>
<input type="text" name="name" value={this.state.name} onChange={this.handleChange.bind(this, 'name')} />
<br />
<label htmlFor="domain">域名:</label>
<input type="text" name="domain" value={this.state.domain} onChange={this.handleChange.bind(this, 'domain')} />
<br />
<input value="提交" type="submit" />
</form>);
}
});
ReactDOM.render(
<MyInput />,
document.getElementById('example')
);
1.2 通过底层DOMname属性实现共享
虽然React 组件中name属性并不是必须的,但添加name属性时,就可以通过这个属性来判断要更新的是哪个组件,进而实现事件处理器的共享。
如,上面的示例中的change事件处理器,可以改写为通过name属性实现:
handleChange: function(event) {
var newState = {};
newState[event.target.name] = event.target.value;
this.setState(newState);
}
1.3 通过React addon实现共享
React 的addon中提供了一个mixin对:React.addons.LinkedStateMixin。React.addons.LinkedStateMixin是一个双向绑定辅助工具,我们可以通过React.addons.LinkedStateMixin解决同样的问题。
React.addons.LinkedStateMixin为组件提供了一个linkState方法,该方法会返回一个对象,对象中包含value和requestChange两个属性。
value会根据name属性从state中获取对应的值requestChange是一个函数,它会使用新值更新同名的state
如,对于this.linkState('name')来说,其返回值如下:
{
value: this.state.name,
requestChange: function(newValue) {
this.setState({
name: newValue
})
}
}
React 为组件提供了一个非DOM属性valueLink,把this.linkState传入这属性后,valueLink会使用对象返回的新value更新组件值,并自动提供一个onChange事件处理器。
我们可以使用React.addons.LinkedStateMixin将前面的示例改写如下:
var MyForm = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {name: 'IT笔录',
domain: 'niefengjun.cn'};
},
handleSubmit: function(event){
event.preventDefault();
alert(this.state.name+'-'+this.state.domain)
},
render: function() {
return (<form onSubmit={this.handleSubmit}>
<label htmlFor="name">名称:</label>
<input type="text" name="name" valueLink={this.linkState('name')} />
<br />
<label htmlFor="domain">域名:</label>
<input type="text" name="domain" valueLink={this.linkState('domain')} />
<br />
<input value="提交" type="submit" />
</form>);
}
});
2. 自定义组件
我们可以组合多个简单组件,然后生成功能更为复杂的表单组件。自定义组件是另一种复用代码的有效方式,可以复用共有的功能,提高开发效率。
编写自定义组件时应当注意,组件的接口要与其它表单组件保持一致,以帮助用户更好的理解代码和使用快速使用组件。
2.1 定义一个组件
接下来,我们将创建一个单选框组件。对于单选框组件来说,其React 的select组件功能类似,因此我们将组件接口与select保持一致。创建这个组件时,要注意以下几点:
- 定义组件时,首先要保证
onChange属性值是函数 - 然后把
defaultValue属性保存到state - 组件的选项
option会做为子元素传递进来,然后进行渲染 - 绑定组件
name、value、checked属性及onChange事件处理器
组件代码如下:
var MyRadio = React.createClass({
propTypes: {
onChange: React.PropTypes.func
},
getInitialState: function() {
return { value:this.props.defaultValue }
},
handleChange: function(event) {
if(this.props.onChange) {
this.props.onChange(event);
}
console.log(this.props)
this.setState({
value: event.target.value
});
},
render: function() {
var children = [];
var value = this.props.value || this.state.value;
React.Children.forEach(this.props.children, function(child, i){
var lable = (
<label>
<input
type = "radio"
name = {this.props.name}
value = {child.props.value}
checked = {child.props.value == value}
onChange = {this.handleChange}
/>
{child.props.children}
<br />
</label>
);
children.push(Object.assign({}, lable, { key: 'label'+i }));
}.bind(this));
return >span>{children}</span>;
}
});
这样我就定义了一个MyRadio组件,这个组件即可以用于“受控组件”又可用于“非受控组件”。
2.2 做为非控组件使用
做为“受控组件”使用时,其操作与操作一个单选框几乎没有区别,onChange事件会被操作radio的选中事件。
var MyForm = React.createClass({
getInitialState: function() {
return { myRadio:'itbilu' }
},
handleChange: function(event) {
this.setState({
myRadio: event.target.value
});
},
handleSubmit: function(event){
event.preventDefault();
alert(this.state.myRadio)
},
render: function() {
return (<form onSubmit={this.handleSubmit}>
<MyRadio name="myRadio" value={this.state.myRadio} onChange={this.handleChange}>
<option value="itbilu">niefengjun.cn</option>
<option value="yijiebuyi">yijiebuyi.com</option>
</MyRadio>
<input value="提交" type="submit" />
</form>);
}
});
2.3 做为非控组件使用
做为“非受控组件”组件使用时,直接通过state取值,而不用通过getDOMNode()获取底层DOM值。
var MyForm = React.createClass({
handleSubmit: function(event){
event.preventDefault();
alert(this.refs.radio.state.value)
},
render: function() {
return (<form onSubmit={this.handleSubmit}>
<MyRadio ref="radio" name="myRadio" defaultValue="yijiebuyi">
<option value="itbilu">niefengjun.cn</option>
<option value="yijiebuyi">yijiebuyi.com</option>
</MyRadio>
<input value="提交" type="submit" />
</form>);
}
});
