說明
支持我們

表單

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 要求一個值,但隨後告訴它使用我們想要的任何值重新渲染。

因此,我們不應該偵聽 input 事件,而應該偵聽 click 事件,此事件會在使用者點選核取方塊或關聯的 <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 中執行