表单

¥Forms

Preact 中的表单与 HTML 中的表单工作方式非常相似。你渲染一个控件,并向其附加一个事件监听器。

¥Forms in Preact work much the same as they do in HTML. You render a control, and attach an event listener to it.

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

¥The main difference is that in most cases the value is not controlled by the DOM node, but by Preact.



受控和非受控组件

¥Controlled & Uncontrolled Components

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

¥When talking about form controls you'll often encounter the words "Controlled Component" and "Uncontrolled Component". The description refers to the way data flow is handled. The DOM has a bidirectional data flow, because every form control will manage the user input themselves. A simple text input will always update its value when a user typed into it.

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

¥A framework like Preact in contrast generally has a unidirectional data flow. The component doesn't manage the value itself there, but something else higher up in the component tree.

// 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 功能的挂载点仍然很有用。在这些情况下,"不受控制" Components 非常适合这项任务。

¥Generally, you should try to use Controlled Components at all times. However, when building standalone Components or wrapping third-party UI libraries, it can still be useful to simply use your component as a mount point for non-preact functionality. In these cases, "Uncontrolled" Components are nicely suited to the task.

这里需要注意的一个问题是,将值设置为 undefinednull 基本上会变得不受控制。

¥One gotcha to note here is that setting the value to undefined or null will essentially become uncontrolled.

创建一个简单的表单

¥Creating A Simple Form

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

¥Let's create a simple form to submit todo items. For this we create a <form>-Element and bind an event handler that is called whenever the form is submitted. We do a similar thing for the text input field, but note that we are storing the value in our class ourselves. You guessed it, we're using a controlled input here. In this example it's very useful, because we need to display the input's value in another element.

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>
    );
  }
}
Run in REPL

选择输入

¥Select Input

<select>-Element 稍微复杂一些,但其工作原理与所有其他表单控件类似:

¥A <select>-Element is a little more involved, but works similar to all other form controls:

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>
    );
  }
}
Run in REPL

复选框和单选按钮

¥Checkboxes & Radio Buttons

在构建受控表单时,复选框和单选按钮 (<input type="checkbox|radio">) 最初可能会引起混乱。这是因为在不受控制的环境中,我们通常会允许浏览器为我们 "toggle" 或 "check" 复选框或单选按钮,监听更改事件并对新值做出反应。然而,这种技术并不能很好地过渡到 UI 始终自动更新以响应状态和属性变化的世界观。

¥Checkboxes and radio buttons (<input type="checkbox|radio">) can initially cause confusion when building controlled forms. This is because in an uncontrolled environment, we would typically allow the browser to "toggle" or "check" a checkbox or radio button for us, listening for a change event and reacting to the new value. However, this technique does not transition well into a world view where the UI is always updated automatically in response to state and prop changes.

演练:假设我们监听复选框上的 "change" 事件,当用户选中或取消选中该复选框时会触发该事件。在我们的更改事件处理程序中,我们将 state 中的值设置为从复选框接收到的新值。这样做将触发组件的重新渲染,这会将复选框的值重新分配给状态中的值。这是不必要的,因为我们只是向 DOM 请求一个值,然后告诉它用我们想要的任何值再次渲染。

¥Walk-Through: Say we listen for a "change" event on a checkbox, which is fired when the checkbox is checked or unchecked by the user. In our change event handler, we set a value in state to the new value received from the checkbox. Doing so will trigger a re-render of our component, which will re-assign the value of the checkbox to the value from state. This is unnecessary, because we just asked the DOM for a value but then told it to render again with whatever value we wanted.

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

¥So, instead of listening for a input event we should listen for a click event, which is fired any time the user clicks on the checkbox or an associated <label>. Checkboxes just toggle between Boolean true and false, so clicking the checkbox or the label, we'll just invert whatever value we have in state, triggering a re-render, setting the checkbox's displayed value to the one we want.

复选框示例

¥Checkbox Example

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>
    );
  }
}
Run in REPL
Preact 中文网 - 粤ICP备13048890号