React—表单及事件处理

作者: 徐国军_plus | 来源:发表于2018-02-24 11:13 被阅读1225次

    表单

    提到React中表单及事件处理,就不得不先介绍一下控组件与非受控组件的概念。

    在HTML中,表单元素与其他元素最大的不同是它自带值或数据,而且在我们的应用中,只要是有表单出现的地方,就会有用户输入,就会有表单事件触发,就会涉及的数据处理。

    在我们用React开发应用时,为了更好地管理应用中的数据,响应用户的输入,编写组件的时候呢,我们就会运用到受控组件与非受控组件这两个概念。

    React推荐我们在绝大多数情况下都使用受控组件。这样可以保证表单的数据在组件的state管理之下,而不是各自独立保有各自的数据。

    受控与非受控组件 Controlled & Uncontrolled

    受控组件:

    一般涉及到表单元素时我们才会使用这种分类方法。受控组件的值由props或state传入,用户在元素上交互或输入内容会引起应用state的改变。在state改变之后重新渲染组件,我们才能在页面中看到元素中值的变化,假如组件没有绑定事件处理函数改变state,用户的输入是不会起到任何效果的,这也就是“受控”的含义所在。

    非受控组件:
    类似于传统的DOM表单控件,用户输入不会直接引起应用state的变化,我们也不会直接为非受控组件传入值。想要获取非受控组件,我们需要使用一个特殊的ref属性,同样也可以使用defaultValue属性来为其指定一次性的默认值。

    来看具体的例子:

    // 受控组件
    class ControlledInput extends React.Component {
      constructor() {
        super()
        this.state = {value: 'Please type here...'}
      }
    
      handleChange(event) {
        console.log('Controlled change:',event.target.value)
        this.setState({value: event.target.value})
      }
    
      render() {
        return (
          <label>
            Controlled Component:
            <input type="text"
                   value={this.state.value}
                   onChange={(e) => this.handleChange(e)}
            />
          </label>
        )
      }
    }
    
    // 非受控组件
    class UncontrolledInput extends React.Component {
      constructor() {
        super()
      }
    
      handleChange() {
        console.log('Uncontrolled change:',this.input.value)
      }
    
      render() {
        return (
            <label>
              Uncontrolled Component:
              <input type="text"
                     defaultValue='Please type here...'
                     ref={(input) => this.input = input}
                     onChange={() =>this.handleChange()}
              />
            </label>
        )
      }
    }
    
    ReactDOM.render(
      <div>
        <UncontrolledInput />
        <ControlledInput />
      </div>
      ,document.getElementById('root'))
    

    通常情况下,React当中所有的表单控件都需要是受控组件。但正如我们对受控组件的定义,想让受控组件正常工作,每一个受控组件我们都需要为其编写事件处理函数,有的时候确实会很烦人,比方说一个注册表单你需要写出所有验证姓名电话邮箱验证码的逻辑,当然也有一些小技巧可以让同一个事件处理函数应用在多个表单组件上,但生产开发中并没有多大实际意义。更有可能我们是在对已有的项目进行重构,除了React之外还有一些别的库需要和表单交互,这时候使用非受控组件可能会更方便一些。

    表单元素

    我们在组件中声明表单元素时,一般都要为表单元素传入应用状态中的值,可以通过state也可以通过props传递,之后需要为其绑定相关事件,例如表单提交,输入改变等。在相关事件触发的处理函数中,我们需要根据表单元素中用户的输入,对应用数据进行相应的操作和改变,来看下面这个例子:

    class ControlledInput extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          value: ""
        }
      }
    
      handleChange(event) {
        this.setState({
          value: event.target.value
          })
      }
    
      render() {
        return <input 
                  type="text" 
                  value={this.state.value} 
                  onChange={ (e) => this.handleChange(e)} 
                />
      }
    }
    
    ReactDOM.render(<ControlledInput />,document.getElementById('root'))
    

    受控组件的输入数据是一直和我们的应用状态绑定的,在上面这个例子中,事件处理函数中一定要有关state的更新操作,这样表单组件才能及时正确响应用户的输入,可以把setState语句注释掉来试验一下。

    textarea:

    HTML

    <textarea>
      Hello there, this is some text in a text area
    </textarea>
    

    JSX

    <textarea value={this.state.value} onChange={this.handleChange} />
    

    这里需要强调一下,JSX中使用的和HTML标签同名的元素并不等同于原生的HTML标签,这只是React内部抽象出来的一种标签的写法,只是看起来一样而已,下面就介绍一下表单元素中,JSX和HTML不一样的,需要注意的地方。

    在HTML中,textarea标签当中的内容都是在其开闭合标签之间的子节点当中的。而在JSX中,为了统一,textarea也可以定义一个名为value的属性,用来传入应用状态中的相关值。

    select

    HTML

    <select>
      <option value="grapefruit">Grapefruit</option>
      <option value="lime">Lime</option>
      <option selected value="coconut">Coconut</option>
      <option value="mango">Mango</option>
    </select>
    

    JSX

    <select value={this.state.value} onChange={this.handleChange}>
        <option value="grapefruit">Grapefruit</option>
        <option value="lime">Lime</option>
        <option value="coconut">Coconut</option>
        <option value="mango">Mango</option>
    </select>
    

    select也是一样,注意这里的写法,同样我们可以为JSX当中的select标签定义value属性,与应用状态中相关数据值相同的option将会被默认选中。

    使用受控组件和非受控组件都是有响应的适用场景的,就拿input来讲,比方说它是一个搜索框,我们需要在应用中实现根据搜索框内容输入异步返回相关搜索建议的功能,那么此处的input就应该是受控组件。而假如它是Todo应用中用来添加新事项的输入框,我们就没有特别的理由需要实时获取其中的数据,只需要在添加事项的事件触发时获取输入框中的值即可,这个地方就可以使用非受控组件。

    事件

    HTML

    <button onclick="activateLasers()">
    Activate Lasers
    </button>
    

    JSX

    <button onClick={activateLasers}>
    Activate Lasers
    </button>
    

    React元素的事件属性几乎与HTML中的事件相关属性相同,不过在React当中,事件相关的属性是以小驼峰的方式命名的。在这里还是要强调一下,React元素中的事件处理也是React内部的抽象封装,这里只是说,我们在JSX中写出来,看上去差不多,并不代表这是HTML原生的事件属性

    // 手动绑定
    this.handleClick = this.handleClick.bind(this);
    // 箭头函数自动绑定
    handleClick = () => {
        console.log('this is:', this);
      }
    

    新版本的React中,我们可以通过类和函数声明React组件,在这两种形式的声明当中,我们都可以为其定义事件处理函数,函数定义的组件只需要在其方法内部再定义事件触发的函数即可;而如果是类声明组件,类定义组件中的自定义方法默认是没有绑定this的,因此加入我们需要在事件处理函数中调用this.setState一类的方法,就必须要手动将this绑定在相应的事件处理函数上。

    代码示例:

    class ControlledInput extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          value: ""
        };
        this.handleChange = this.handleChange.bind(this);
      }
    
      handleChange(event) {
        this.setState({
          value: event.target.value
          })
      }
    
      render() {
        return <input 
                  type="text" 
                  value={this.state.value} 
                  onChange={ this.handleChange} 
                />
      }
    }
    
    ReactDOM.render(<ControlledInput />,document.getElementById('root'))
    

    相关文章

      网友评论

        本文标题:React—表单及事件处理

        本文链接:https://www.haomeiwen.com/subject/wncqxftx.html