您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

React 表单

React 表单

表单不同于其他 HTML 元素,因为它要响应的交互,不同的状态,所以在 React 里面会有点特殊。

在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。例如这个纯 HTML 表单只接受:

<form>
  <label>
    名字:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="提交" />
</form>

此表单具有认的 HTML 表单行为,即在提交表单后浏览到新。如果你在 React 中执行相同的,它依然有效。但大多数情况下,使用 JavaScript 可以很方便的处理表单的提交, 同时还可以访问填写的表单数据。实现这种的标准方式是使用“受控组件”。

状态

表单元素有这么几种属于状态的:

value,对应 <input> 和 <textarea> 所有

checked,对应类型为 check@R_344_2@ 和 ra 的 <input> 所有

selected,对应 <option> 所有

在 HTML 中 <textarea> 的值可以由子节点(文本)赋值,但是在 React 中,要用 value 来设置。

表单元素包含以上任意一种状态都 onChange 事件监听状态值的更改。

针对这些状态不同的处理策略,表单元素在 React 里面有两种表现形式。

受控组件

在 HTML 中,表单元素(如<input>、 <textarea> 和 <select>)之类的表单元素通常自己维护 state,并根据输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 中,并且只能通过使用 setState()来更新。

我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

例如,如果我们想让前示例在提交时打印出,我们可以将表单写为受控组件:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

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

  handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

由于在表单元素上设置了 value ,因此的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 handlechange 在每次按键时都会执行并更新 React 的 state,因此的值将随着输入而更新。

对于受控组件来说,每个 state 突变都有相关的处理。这使得或验证输入变得简单。例如,如果我们要强制要求所有都用大写字母书写,我们可以将 handlechange 改写为:

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

对于设置了上面提到的对应“状态“值的表单元素就是受控表单组件,比如:

render: function() {
    return <input type="text" value="hello"/>;
}

受控的表单组件,它所有状态更改涉及 UI 的变更都由 React 来控制(状态绑定 UI)。比如上面里的 <input> 输入框,输入,输入的不会(输入框总是状态 value的值 hello),这有点颠覆我们的认知了,所以说这是受控组件,不是原来认的表单元素了。

如果你希望输入的反馈到输入框,就要用 onChange 事件改变状态 value 的值:

getInitialState: function() {
    return {value: 'hello'};
},
handleChange: function(event) {
    this.setState({value: event.target.value});
},
render: function() {
    var value = this.state.value;
    return <input type="text" value={value} onChange={this.handleChange} />;
}

使用这种模式非常容易实现类似对输入的验证,或者对交互做额外的处理,比如截断最多输入140个字符:

handleChange: function(event) {
    this.setState({value: event.target.value.substr(0, 140)});
}

非受控组件

和受控组件相对,如果表单元素没有设置自己的“状态”,或者值设置为 null,这时候就是非受控组件。

它的表现就符合普通的表单元素,正常响应的操作。

同样,你也可以绑定 onChange 事件处理交互。

如果你想要给“状态”设置认值,就要用 React 提供的特殊 defaultValue,对于 checked 会有 defaultChecked,<option> 也是使用 defaultValue。

为什么要有受控组件?

引入受控组件不是说它有什么好处,而是因为 React 的 UI 渲染机制,对于表单元素不得不引入这一特殊的处理方式。

在浏览器 DOM 里面是有区分 attribute 和 property 的。attribute 是在 HTML 里指定的,而每个 HTML 元素在 JS 对应是 DOM 节点对象,这个对象拥有的就是 property(可以在 console 里展开 DOM 节点对象看一下,HTML attributes 只是对应其中的一部分),attribute 对应的 property 会从 attribute 拿到初始值,有些会有相同的,但是有些会不一样,比如 attribute class 对应的 property 就是 className。(详细解释:,)

回到 React 里的 <input> 输入框,当输入的时候,输入框的 value property 会改变,但是 value attribute 依然会是 HTML 上指定的值(attribute 要用 setAttribute 去更改)。

React 组件必须呈现这个组件的状态视图,这个视图 HTML 是由 render ,所以对于

render: function() {
    return <input type="text" value="hello"/>;
}

在任意时刻,这个视图总是返回 hello 的输入框。

<select>

在 HTML 中 <select> 指定选中项都是通过对应 <option> 的 selected 来做的,但是在 React 成统一使用 value。

所以没有 selected 的状态。

<select value="B">
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cran</option>
</select>

你可以通过传递数组指定多个选中项:<select multiple={true} value={['B', 'C']}>

textarea

在 HTML 中, <textarea> 元素通过其子元素定义其文本:

<textarea>
  你好, 这是在textarea里的文本
</textarea>

而在 React 中,<textarea> 使用 value 代替。这样,可以使得使用 <textarea> 的表单和使用单行 input 的表单非常类似:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '请撰写一篇关于你喜欢的 DOM 元素的.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

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

  handleSubmit(event) {
    alert('提交的: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          :
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

请注意,this.state.value 初始化于构造中,因此文本区域认有初值。

select

在 HTML 中,<select> 创建下拉列表。例如,如下 HTML 创建了水果相关的下拉列表:

<select>
  <option value="grapefruit">葡萄柚</option>
  <option value="lime">酸橙</option>
  <option selected value="coconut">椰子</option>
  <option value="mango">芒果</option>
</select>

请注意,由于 selected 的缘故,椰子选项认被选中。React 并不会使用 selected ,而是在根 select 上使用 value 。这在受控组件中更便捷,因为您只需要在根中更新它。例如:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

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

  handleSubmit(event) {
    alert('你喜欢的风味是: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择你喜欢的风味:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">葡萄柚</option>
            <option value="lime">酸橙</option>
            <option value="coconut">椰子</option>
            <option value="mango">芒果</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

总的来说,这使得 <input type="text">, <textarea> 和 <select> 之类的都非常相似—它们都接受 value ,你可以使用它来实现受控组件。

注意

你可以将数组传递到 value 中,以在 select 中选择多个选项:

<select multiple={true} value={['B', 'C']}>

input

在 HTML 中,<input type=“file”> 允许从存储设备中选择或多个,将其到服务器,或通过使用 JavaScript 的  进行控制。

<input type="file" />

因为它的 value 只读,所以它是 React 中的非受控组件。将与其他非受控组件在后续文档中一起讨论。

处理多个输入

当需要处理多个 input 元素时,我们可以给每个元素 name ,并让处理根据 event.target.name 的值选择要执行的操作。

例如:

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'check@R_344_2@' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          参与:
          <input
            name="isGoing"
            type="check@R_344_2@"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          来宾人数:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

这里使用了 ES6 的语法更新给定输入对应的 state 值:

例如:

this.setState({
  [name]: value
});

等同 ES5:

var partialState = {};
partialState[name] = value;
this.setState(partialState);

另外,由于 setState() 将部分 state 合并到当前 state, 只需它更改部分 state 即可。

受控输入空值

在受控组件上指定 value 的 prop 可以防止更改输入。如果指定了 value,但输入仍可编辑,则可能是意外地将value 设置为 undefined 或 null。

下面的演示了这一点。(输入最初被锁定,但在短时间延迟后变为可编辑。)

ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

受控组件的替代品

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理,并通过 React 组件传递所有的输入 state。当你将之前的库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。

成熟的案

如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整案,使用  是不错的选择。然而,它也是建立在受控组件和管理 state 的基础之上 —— 所以不要忽视学习它们。


联系我
置顶