帮助
支持我们

表单

Preact 中的表单与 HTML 中的工作方式大致相同。您渲染一个控件,并为其附加一个事件侦听器。

主要区别在于,在大多数情况下,value 不是由 DOM 节点控制的,而是由 Preact 控制的。



受控和不受控组件

在讨论表单控件时,您经常会遇到“受控组件”和“不受控组件”这两个词。该描述指的是数据流的处理方式。DOM 具有双向数据流,因为每个表单控件都将自己管理用户输入。当用户键入简单文本输入时,它总是会更新其值。

相比之下,像 Preact 这样的框架通常具有单向数据流。组件本身不管理值,而是组件树中更高层次的其他内容管理值。

// Uncontrolled, because Preact doesn't set the value
<input onInput={myEventHandler} />;

// Controlled, because Preact manages the input's value now
<input value={someValue} onInput={myEventHandler} />;

通常,您应该始终尝试使用受控组件。但是,在构建独立组件或包装第三方 UI 库时,仍然可以简单地将您的组件用作非 preact 功能的安装点。在这些情况下,“不受控”组件非常适合这项任务。

这里需要注意的一点是,将值设置为 undefinednull 本质上将变得不受控制。

创建简单表单

让我们创建一个简单的表单来提交待办事项。为此,我们创建一个 <form> 元素并绑定一个事件处理程序,该处理程序将在每次提交表单时调用。我们对文本输入字段执行类似的操作,但请注意,我们自己将值存储在我们的类中。您猜对了,我们在这里使用的是受控输入。在此示例中,它非常有用,因为我们需要在另一个元素中显示输入的值。

class TodoForm extends Component {
  state = { value: '' };

  onSubmit = e => {
    alert("Submitted a todo");
    e.preventDefault();
  }

  onInput = e => {
    this.setState({ value: e.currentTarget.value })
  }

  render(_, { value }) {
    return (
      <form onSubmit={this.onSubmit}>
        <input type="text" value={value} onInput={this.onInput} />
        <p>You typed this value: {value}</p>
        <button type="submit">Submit</button>
      </form>
    );
  }
}
在 REPL 中运行

选择输入

<select> 元素涉及更多内容,但其工作方式与所有其他表单控件类似

class MySelect extends Component {
  state = { value: '' };

  onChange = e => {
    this.setState({ value: e.currentTarget.value });
  }

  onSubmit = e => {
    alert("Submitted " + this.state.value);
    e.preventDefault();
  }

  render(_, { value }) {
    return (
      <form onSubmit={this.onSubmit}>
        <select value={value} onChange={this.onChange}>
          <option value="A">A</option>
          <option value="B">B</option>
          <option value="C">C</option>
        </select>
        <button type="submit">Submit</button>
      </form>
    );
  }
}
在 REPL 中运行

复选框和单选按钮

构建受控表单时,复选框和单选按钮 (<input type="checkbox|radio">) 可能会最初造成混淆。这是因为在不受控环境中,我们通常允许浏览器为我们“切换”或“选中”复选框或单选按钮,监听更改事件并对新值做出反应。但是,这种技术无法很好地过渡到 UI 始终根据状态和属性更改自动更新的世界观中。

演练:假设我们监听复选框上的“更改”事件,该事件在用户选中或取消选中复选框时触发。在我们的更改事件处理程序中,我们将 state 中的值设置为从复选框接收到的新值。这样做将触发我们组件的重新渲染,这将重新将复选框的值分配给 state 中的值。这是不必要的,因为我们刚刚向 DOM 询问了一个值,但随后告诉它用我们想要的任何值重新渲染。

因此,我们应该监听 click 事件而不是 input 事件,该事件在用户点击复选框或关联的 <label> 时触发。复选框只是在布尔值 truefalse 之间切换,因此点击复选框或标签,我们将反转 state 中的任何值,触发重新渲染,将复选框的显示值设置为我们想要的值。

复选框示例

class MyForm extends Component {
  toggle = e => {
      let checked = !this.state.checked;
      this.setState({ checked });
  };

  render(_, { checked }) {
    return (
      <label>
        <input
          type="checkbox"
          checked={checked}
          onClick={this.toggle}
        />
        check this box
      </label>
    );
  }
}
在 REPL 中运行