表单
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 询问了一个值,但随后告诉它用我们想要的任何值重新渲染。
因此,我们应该监听 click
事件而不是 input
事件,该事件在用户点击复选框或关联的 <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 中运行