React 表单-表单组件的高级用法

 2016年02月18日    2078     声明


随着应用规模的扩大,应该考虑对相同功能进行抽象,以提高代码的复用率和开发效率。React 表单组件中,我们可以对多个表单元素共享事件处理器,或将功能相同的组件编写为自定义组件。


  1. 多表单元素共享事件处理器
  2. 自定义组件


1. 多表单元素共享事件处理器

在创建表单组件时,有时多个组件的事件处理器功能相同或相似,这时我们可以在多个组件间共享一个事件处理器,而不用分别编写事件处理器。共享事件处理器时,需要解决参数传递的问题。以下几种传递参数的方法:

1.1 通过.bind()方法共享

.bind()方法会为指定对象创建一个函数的实例,调用.bind()方法的时可以传递参数。

如,我们可以通过.bind()在多个组件中共享change事件的事件处理器,并根据组件的不同传递不同的参数:

var MyForm = React.createClass({
  getInitialState: function() {
    return {name: 'IT笔录',
      domain: 'itbilu.com'};
  },
  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.LinkedStateMixinReact.addons.LinkedStateMixin是一个双向绑定辅助工具,我们可以通过React.addons.LinkedStateMixin解决同样的问题。

React.addons.LinkedStateMixin为组件提供了一个linkState方法,该方法会返回一个对象,对象中包含valuerequestChange两个属性。

  • 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: 'itbilu.com'};
  },
  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会做为子元素传递进来,然后进行渲染
  • 绑定组件namevaluechecked属性及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">itbilu.com</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">itbilu.com</option>
        <option value="yijiebuyi">yijiebuyi.com</option>
      </MyRadio>
      <input value="提交" type="submit" />
    </form>);
  }
});