# 2-5-表单及事件处理

## 表单

记得在之前，组件类型一课中，我们介绍了一组受控组件与非受控组件的概念。受控与非受控组件就是专门适用于React当中的表单元素的。

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

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

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

### 表单元素

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

```jsx
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={() => this.handleChange()} 
            />
  }
}
```

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

**textarea**

**HTML**

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

**JSX**

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

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

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

**select**

**HTML**

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

**JSX**

```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**

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

**JSX**

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

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

React元素相关的事件属性我们可以在官方文档的这个页面中找到[https://facebook.github.io/react/docs/events.html](https://facebook.github.io/react/docs/events.html)

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

## 动手试试

想要亲自实践的话，可以试着编写一个非常简单的 Markdown 编辑器：

```jsx
class MarkdownEditor extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {value: MD_CONTENT};
  }

  handleChange(e) {
    this.setState({value: e.target.value});
  }

  getRawMarkup() {
    var md = new Remarkable();
    return { __html: md.render(this.state.value) };
  }

  render() {
    return (
      <div className="MarkdownEditor">
        <div className="left">
          <h3>Input</h3>
          <textarea
            onChange={this.handleChange}
            defaultValue={this.state.value} />
        </div>
        <div className="right">
        <h3>Output</h3>
        <div
          className="content"
          dangerouslySetInnerHTML={this.getRawMarkup()}
        />
        </div>
      </div>
    );
  }
}
```

在 Codepen 上试试。
