几乎每个应用程序都需要在某些时候接受用户的输入,而这通常是通过古老的HTML表单及其输入控件的集合来实现。如果你最近开始学习React,你可能已经到了这样的地步:"那我该如何使用表单?"
本文将引导你了解在React中使用表单的基本知识,让用户添加或编辑信息。我们将看看两种不同的输入控件的工作方式,以及每种方式的优点和缺点。我们还将看一下如何处理验证,以及一些用于更高级用例的第三方库。
不受控制的输入
在React中处理表单的最基本方式是使用所谓的 "非控制性 "表单输入。这意味着React并不跟踪输入的状态。HTML输入元素作为DOM的一部分自然会跟踪它们自己的状态,所以当表单被提交时,我们必须从DOM元素本身读取值。
为了做到这一点,React允许我们创建一个 "ref"(引用),与一个元素相关联,让我们访问底层的DOM节点。让我们看看如何做到这一点。
class SimpleForm extends React.Component {
constructor(props) {
super(props);
// create a ref to store the DOM element
this.nameEl = React.createRef();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
alert(this.nameEl.current.value);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>Name:
<input type="text" ref={this.nameEl} />
</label>
<input type="submit" name="Submit" />
</form>
)
}
}
正如你在上面看到的,对于一个基于类的组件,你通过调用React.createRef ,在构造函数中初始化一个新的引用,并将其分配给一个实例属性,所以它在组件的生命周期内是可用的。
为了将ref与一个输入关联起来,它被作为特殊的ref 属性传递给元素。一旦这样做了,输入的底层DOM节点就可以通过this.nameEl.current 。
让我们看看这在一个功能组件中是怎样的。
function SimpleForm(props) {
const nameEl = React.useRef(null);
const handleSubmit = e => {
e.preventDefault();
alert(nameEl.current.value);
};
return (
<form onSubmit={handleSubmit}>
<label>Name:
<input type="text" ref={nameEl} />
</label>
<input type="submit" name="Submit" />
</form>
);
}
除了把createRef 换成useRef 钩子之外,这里没有什么区别。
示例:登录表单
function LoginForm(props) {
const nameEl = React.useRef(null);
const passwordEl = React.useRef(null);
const rememberMeEl = React.useRef(null);
const handleSubmit = e => {
e.preventDefault();
const data = {
username: nameEl.current.value,
password: passwordEl.current.value,
rememberMe: rememberMeEl.current.checked,
}
// Submit form details to login endpoint etc.
// ...
};
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="username" ref={nameEl} />
<input type="password" placeholder="password" ref={passwordEl} />
<label>
<input type="checkbox" ref={rememberMeEl} />
Remember me
</label>
<button type="submit" className="myButton">Login</button>
</form>
);
}
虽然不受控制的输入对于快速和简单的表单来说效果不错,但它们确实有一些缺点。正如你可能从上面的代码中注意到的,我们必须在任何时候从输入元素中读取值。这意味着我们不能在用户输入时对字段进行即时验证,也不能做一些事情,比如强制执行自定义输入格式,有条件地显示或隐藏表单元素,或者禁用/启用提交按钮。
幸运的是,在React中有一种更复杂的方式来处理输入。
受控的输入
当React负责维护和设置其状态时,一个输入被称为 "受控"。状态与输入的值保持同步,这意味着改变输入将更新状态,而更新状态将改变输入。
让我们通过一个例子来看看这是什么样子的。
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = { name: '' };
this.handleInput = this.handleInput.bind(this);
}
handleInput(event) {
this.setState({
name: event.target.value
});
}
render() {
return (
<input type="text" value={this.state.name} onChange={this.handleInput} />
);
}
}
正如你所看到的,我们设置了一种循环的数据流:状态到输入值,在变化事件中到状态,然后再返回。这个循环允许我们对输入进行大量的控制,因为我们可以对值的变化做出反应。正因为如此,受控输入不会受到非受控输入的限制,从而开启了以下的可能性。
- 即时输入验证:我们可以给用户即时反馈,而不需要等待他们提交表单(例如,如果他们的密码不够复杂的话)
- 即时输入格式化:我们可以为货币输入添加适当的分隔符,或者为电话号码即时分组
- 有条件地禁止表单提交:我们可以在满足某些条件后启用提交按钮(例如,用户同意条款和条件)。
- 动态生成新的输入:我们可以根据用户之前的输入为表单添加额外的输入(例如,在酒店预订中添加额外人员的详细信息)。
验证
正如我上面提到的,受控组件的连续更新循环使我们有可能在用户输入时对输入进行连续验证。连接到输入的onChange 事件的处理程序将在每次击键时被触发,允许你即时验证或格式化数值。
继续阅读SitePoint上的《在React中使用表单》。