表單
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 功能的掛載點仍然很有用。在這些情況下,「非受控」元件非常適合這項任務。
這裡要注意的一點是,將值設定為
undefined
或null
本質上會變成非受控。
建立一個簡單的表單
讓我們建立一個簡單的表單來提交待辦事項。為此,我們建立一個 <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>
時觸發。核取方塊只會在布林值 true
和 false
之間切換,因此點選核取方塊或標籤,我們只會反轉 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 中執行